Go WebSocket

2022-05-13 17:42 更新

WebSocket是HTML5的重要特性,它實(shí)現(xiàn)了基于瀏覽器的遠(yuǎn)程socket,它使瀏覽器和服務(wù)器可以進(jìn)行全雙工通信,許多瀏覽器(Firefox、Google Chrome和Safari)都已對(duì)此做了支持。

在WebSocket出現(xiàn)之前,為了實(shí)現(xiàn)即時(shí)通信,采用的技術(shù)都是“輪詢”,即在特定的時(shí)間間隔內(nèi),由瀏覽器對(duì)服務(wù)器發(fā)出HTTP Request,服務(wù)器在收到請(qǐng)求后,返回最新的數(shù)據(jù)給瀏覽器刷新,“輪詢”使得瀏覽器需要對(duì)服務(wù)器不斷發(fā)出請(qǐng)求,這樣會(huì)占用大量帶寬。

WebSocket采用了一些特殊的報(bào)頭,使得瀏覽器和服務(wù)器只需要做一個(gè)握手的動(dòng)作,就可以在瀏覽器和服務(wù)器之間建立一條連接通道。且此連接會(huì)保持在活動(dòng)狀態(tài),你可以使用JavaScript來向連接寫入或從中接收數(shù)據(jù),就像在使用一個(gè)常規(guī)的TCP Socket一樣。它解決了Web實(shí)時(shí)化的問題,相比傳統(tǒng)HTTP有如下好處:

  • 一個(gè)Web客戶端只建立一個(gè)TCP連接
  • Websocket服務(wù)端可以推送(push)數(shù)據(jù)到web客戶端.
  • 有更加輕量級(jí)的頭,減少數(shù)據(jù)傳送量

WebSocket URL的起始輸入是ws://或是wss://(在SSL上)。下圖展示了WebSocket的通信過程,一個(gè)帶有特定報(bào)頭的HTTP握手被發(fā)送到了服務(wù)器端,接著在服務(wù)器端或是客戶端就可以通過JavaScript來使用某種套接口(socket),這一套接口可被用來通過事件句柄異步地接收數(shù)據(jù)。


WebSocket原理

WebSocket的協(xié)議頗為簡單,在第一次handshake通過以后,連接便建立成功,其后的通訊數(shù)據(jù)都是以”\x00″開頭,以”\xFF”結(jié)尾。在客戶端,這個(gè)是透明的,WebSocket組件會(huì)自動(dòng)將原始數(shù)據(jù)“掐頭去尾”。

瀏覽器發(fā)出WebSocket連接請(qǐng)求,然后服務(wù)器發(fā)出回應(yīng),然后連接建立成功,這個(gè)過程通常稱為“握手” (handshaking)。請(qǐng)看下面的請(qǐng)求和反饋信息:


在請(qǐng)求中的"Sec-WebSocket-Key"是隨機(jī)的,對(duì)于整天跟編碼打交到的程序員,一眼就可以看出來:這個(gè)是一個(gè)經(jīng)過base64編碼后的數(shù)據(jù)。服務(wù)器端接收到這個(gè)請(qǐng)求之后需要把這個(gè)字符串連接上一個(gè)固定的字符串:

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

即:f7cb4ezEAl6C3wRaU6JORA==連接上那一串固定字符串,生成一個(gè)這樣的字符串:

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

對(duì)該字符串先用 sha1安全散列算法計(jì)算出二進(jìn)制的值,然后用base64對(duì)其進(jìn)行編碼,即可以得到握手后的字符串:

rE91AJhfC+6JdVcVXOGJEADEJdQ=

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

Go實(shí)現(xiàn)WebSocket

Go語言標(biāo)準(zhǔn)包里面沒有提供對(duì)WebSocket的支持,但是在由官方維護(hù)的go.net子包中有對(duì)這個(gè)的支持,你可以通過如下的命令獲取該包:

go get code.google.com/p/go.net/websocket

WebSocket分為客戶端和服務(wù)端,接下來我們將實(shí)現(xiàn)一個(gè)簡單的例子:用戶輸入信息,客戶端通過WebSocket將信息發(fā)送給服務(wù)器端,服務(wù)器端收到信息之后主動(dòng)Push信息到客戶端,然后客戶端將輸出其收到的信息,客戶端的代碼如下:

<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>

可以看到客戶端JS,很容易的就通過WebSocket函數(shù)建立了一個(gè)與服務(wù)器的連接sock,當(dāng)握手成功后,會(huì)觸發(fā)WebScoket對(duì)象的onopen事件,告訴客戶端連接已經(jīng)成功建立。客戶端一共綁定了四個(gè)事件。

  • 1)onopen 建立連接后觸發(fā)
  • 2)onmessage 收到消息后觸發(fā)
  • 3)onerror 發(fā)生錯(cuò)誤時(shí)觸發(fā)
  • 4)onclose 關(guān)閉連接時(shí)觸發(fā)

我們服務(wù)器端的實(shí)現(xiàn)如下:

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)
    }
}

當(dāng)客戶端將用戶輸入的信息Send之后,服務(wù)器端通過Receive接收到了相應(yīng)信息,然后通過Send發(fā)送了應(yīng)答信息。


通過上面的例子我們看到客戶端和服務(wù)器端實(shí)現(xiàn)WebSocket非常的方便,Go的源碼net分支中已經(jīng)實(shí)現(xiàn)了這個(gè)的協(xié)議,我們可以直接拿來用,目前隨著HTML5的發(fā)展,我想未來WebSocket會(huì)是Web開發(fā)的一個(gè)重點(diǎn),我們需要儲(chǔ)備這方面的知識(shí)。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)