鍍金池/ 教程/ GO/ 8.2 WebSocket
7 文本處理
3 Web基礎(chǔ)
14 擴展Web框架
10.4 小結(jié)
2.2 Go基礎(chǔ)
2.8 總結(jié)
6.1 session和cookie
5.5 使用beedb庫進行ORM開發(fā)
8.3 REST
13.6 小結(jié)
5.4 使用PostgreSQL數(shù)據(jù)庫
14.6 pprof支持
14.1 靜態(tài)文件支持
11.2 使用GDB調(diào)試
7.7 小結(jié)
1 GO環(huán)境配置
14.5 多語言支持
7.1 XML處理
1.5 總結(jié)
13 如何設(shè)計一個Web框架
14.3 表單及驗證支持
12 部署與維護
10 國際化和本地化
1.1 Go 安裝
6.2 Go如何使用session
5.6 NOSQL數(shù)據(jù)庫操作
6.5 小結(jié)
9.4 避免SQL注入
12.1 應(yīng)用日志
4.2 驗證表單的輸入
10.1 設(shè)置默認(rèn)地區(qū)
1.3 Go 命令
9.6 加密和解密數(shù)據(jù)
4.1 處理表單的輸入
4.4 防止多次遞交表單
11.3 Go怎么寫測試用例
8 Web服務(wù)
12.3 應(yīng)用部署
5.7 小結(jié)
12.5 小結(jié)
11 錯誤處理,調(diào)試和測試
9.2 確保輸入過濾
14.2 Session支持
6.4 預(yù)防session劫持
12.4 備份和恢復(fù)
8.1 Socket編程
13.1 項目規(guī)劃
13.4 日志和配置設(shè)計
7.6 字符串處理
13.2 自定義路由器設(shè)計
6.3 session存儲
3.4 Go的http包詳解
8.2 WebSocket
10.3 國際化站點
7.5 文件操作
7.4 模板處理
9.1 預(yù)防CSRF攻擊
13.3 controller設(shè)計
2.6 interface
14.4 用戶認(rèn)證
2.3 流程和函數(shù)
附錄A 參考資料
11.1 錯誤處理
9.5 存儲密碼
9.3 避免XSS攻擊
12.2 網(wǎng)站錯誤處理
6 session和數(shù)據(jù)存儲
2.4 struct類型
3.3 Go如何使得Web工作
2.5 面向?qū)ο?/span>
3.1 Web工作方式
1.2 GOPATH與工作空間
2.1 你好,Go
9.7 小結(jié)
13.5 實現(xiàn)博客的增刪改
7.2 JSON處理
10.2 本地化資源
7.3 正則處理
2 Go語言基礎(chǔ)
5.1 database/sql接口
4.5 處理文件上傳
8.5 小結(jié)
4.3 預(yù)防跨站腳本
5.3 使用SQLite數(shù)據(jù)庫
14.7 小結(jié)
3.2 Go搭建一個Web服務(wù)器
2.7 并發(fā)
5 訪問數(shù)據(jù)庫
4 表單
3.5 小結(jié)
1.4 Go開發(fā)工具
11.4 小結(jié)
9 安全與加密
5.2 使用MySQL數(shù)據(jù)庫
4.6 小結(jié)
8.4 RPC

8.2 WebSocket

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

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

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

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

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

http://wiki.jikexueyuan.com/project/go-web-programming/images/8.2.websocket.png" alt="" />

圖8.2 WebSocket原理圖

WebSocket原理

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

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

http://wiki.jikexueyuan.com/project/go-web-programming/images/8.2.websocket2.png" alt="" />

圖8.3 WebSocket的request和response信息

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

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

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

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

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

rE91AJhfC+6JdVcVXOGJEADEJdQ=

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

Go實現(xiàn)WebSocket

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

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

WebSocket分為客戶端和服務(wù)端,接下來我們將實現(xiàn)一個簡單的例子:用戶輸入信息,客戶端通過WebSocket將信息發(fā)送給服務(wù)器端,服務(wù)器端收到信息之后主動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ù)建立了一個與服務(wù)器的連接sock,當(dāng)握手成功后,會觸發(fā)WebScoket對象的onopen事件,告訴客戶端連接已經(jīng)成功建立??蛻舳艘还步壎怂膫€事件。

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

我們服務(wù)器端的實現(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)答信息。

http://wiki.jikexueyuan.com/project/go-web-programming/images/8.2.websocket3.png" alt="" />

圖8.4 WebSocket服務(wù)器端接收到的信息

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

上一篇:8 Web服務(wù)下一篇:9 安全與加密