1. 程式人生 > >go web 之socket 和 websocket 學習

go web 之socket 和 websocket 學習

socket 介紹

socket 是客戶端和伺服器連線的橋樑,socket 用於web瀏覽器和伺服器的通訊,socket是一種檔案描述符。
socket 分為兩種 流式socket資料報式socket
1. 流式socket是一種面向連線的Socket,針對於面向連線的TCP服務應用
2. 資料報式socket是一種無連線的socket,對應於無連線的UDP服務應用

socket通訊

socket 的通訊依賴於三個部分IP協議, 這三部分可以標誌一個程序
網路層的“ip地址”可以唯一標識網路中的主機,
而傳輸層的“協議+埠”可以唯一標識主機中的應用程式(程序)。
這樣利用三元組(ip地址,協議,埠)就可以標識網路的程序了,網路中需要互相通訊的程序,就可以利用這個標誌在他們之間進行互動。

go 服務端的簡單實現

package main

import (
    "net"
    "os"
    "fmt"
    "time"
)

func main() {
    service :=":7077"
    // 獲得tcpAdd ,其中包括ip 和埠
    /*
    type TCPAddr struct {
    IP IP
    Port int
    }*/
    tcpAddr,err := net.ResolveTCPAddr("tcp4",service)
    checkerror(err)
    //監聽埠號
    listener,err:=net.ListenTCP("tcp"
,tcpAddr) checkerror(err) for { //通過Accept接受連線 conn, err := listener.Accept() if err != nil { continue } //使用goroutine 處理一個連線,實現併發 go handleClient(conn) } } func handleClient(conn net.Conn){ defer conn.Close() daytime:= time.Now().String() conn.Write([]byte
(daytime)) } func checkerror(err error) { if err != nil{ fmt.Fprintf(os.Stderr,"error: %s",err) os.Exit(1) } }

websocket簡介

WebSocket是HTML5的重要特性,它實現了基於瀏覽器的遠端socket,它使瀏覽器和伺服器可以進行全雙工通訊,許多瀏覽器(Firefox、Google Chrome和Safari)都已對此做了支援。

在WebSocket出現之前,為了實現即時通訊,採用的技術都是“輪詢”(見socket程式碼),即在特定的時間間隔內,由瀏覽器對伺服器發出HTTP Request,伺服器在收到請求後,返回最新的資料給瀏覽器重新整理,“輪詢”使得瀏覽器需要對伺服器不斷髮出請求,這樣會佔用大量頻寬。

WebSocket的協議頗為簡單,在第一次handshake通過以後,連線便建立成功,其後的通訊資料都是以”\x00″開頭,以”\xFF”結尾。在客戶端,這個是透明的,WebSocket元件會自動將原始資料“掐頭去尾”。

瀏覽器發出WebSocket連線請求,然後伺服器發出迴應,然後連線建立成功,這個過程通常稱為“握手” (handshaking)。請看下面的請求和反饋資訊:
HTTP資訊

// 告訴伺服器,發起的是websocket 請求
Upgrade: websocket
Connection: Upgrade

Sec-WebSocket-Key 是經過base64編碼後的資料,是瀏覽器隨機生成的

伺服器端接收到這個請求之後需要把這個字串連線上一個固定的字串:

258EAFA5-E914-47DA-95CA-C5AB0DC85B11

即:f7cb4ezEAl6C3wRaU6JORA==連線上那一串固定字串,生成一個這樣的字串:

f7cb4ezEAl6C3wRaU6JORA==258EAFA5-E914-47DA-95CA-C5AB0DC85B11

對該字串先用 sha1安全雜湊演算法計算出二進位制的值,然後用base64對其進行編碼,即可以得到握手後的字串:

rE91AJhfC+6JdVcVXOGJEADEJdQ=

將之作為響應頭Sec-WebSocket-Accept的值反饋給客戶端。

簡單例子

客戶端

<html>
<head></head>
<body>
    <script type="text/javascript">
        var sock = null;
        var wsuri = "ws://127.0.0.1:1234";

        window.onload = function() {

            console.log("onload");

            sock = new WebSocket(wsuri);

            sock.onopen = function() {
                console.log("connected to " + wsuri);
            }

            sock.onclose = function(e) {
                console.log("connection closed (" + e.code + ")");
            }

            sock.onmessage = function(e) {
                console.log("message received: " + e.data);
            }
        };

        function send() {
            var msg = document.getElementById('message').value;
            sock.send(msg);
        };
    </script>
    <h1>WebSocket Echo Test</h1>
    <form>
        <p>
            Message: <input id="message" type="text" value="Hello, world!">
        </p>
    </form>
    <button onclick="send();">Send Message</button>
</body>
</html>

服務端:

package main

import (
    "code.google.com/p/go.net/websocket"
    "fmt"
    "log"
    "net/http"
)

func Echo(ws *websocket.Conn) {
    var err error

    for {
        var reply string

        if err = websocket.Message.Receive(ws, &reply); err != nil {
            fmt.Println("Can't receive")
            break
        }

        fmt.Println("Received back from client: " + reply)

        msg := "Received:  " + reply
        fmt.Println("Sending to client: " + msg)

        if err = websocket.Message.Send(ws, msg); err != nil {
            fmt.Println("Can't send")
            break
        }
    }
}

func main() {
    http.Handle("/", websocket.Handler(Echo))

    if err := http.ListenAndServe(":1234", nil); err != nil {
        log.Fatal("ListenAndServe:", err)
    }
}

至此一個簡單的websocket就實現了,

socket 和 websocket 比較

socket 基於HTTP , 客戶端和伺服器完成一次請求和處理後即斷開連線。
通過for 輪詢,不斷地建立HTTP連線,然後處理。
websocket 最大的特點就是客戶端可以主動向服務端傳送資訊。
並且websocket是基於HTTP的持久連線。只需要經過一次HTTP請求,就可以做到源源不斷的資訊傳送了