WebSocket
前言
WebSocket是HTML5的重要特性,它實現了基於瀏覽器的遠程socket,它使瀏覽器和服務器能夠進行全雙工通訊,許多瀏覽器(Firefox、Google Chrome和Safari)都已對此作了支持。javascript
在WebSocket出現以前,爲了實現即時通訊,採用的技術都是「輪詢」,即在特定的時間間隔內,由瀏覽器對服務器發出HTTP Request,服務器在收到請求後,返回最新的數據給瀏覽器刷新,「輪詢」使得瀏覽器須要對服務器不斷髮出請求,這樣會佔用大量帶寬。 html
WebSocket採用了一些特殊的報頭,使得瀏覽器和服務器只須要作一個握手的動做,就能夠在瀏覽器和服務器之間創建一條鏈接通道。且此鏈接會保持在活動狀態,你可使用JavaScript來向鏈接寫入或從中接收數據,就像在使用一個常規的TCP Socket同樣。它解決了Web實時化的問題,相比傳統HTTP有以下好處:java
WebSocket URL的起始輸入是ws://或是wss://(在SSL上)。一個帶有特定報頭的HTTP握手被髮送到了服務器端,接着在服務器端或是客戶端就能夠經過JavaScript來使用某種套接口(socket),這一套接口可被用來經過事件句柄異步地接收數據。git
WebSocket的協議頗爲簡單,在第一次handshake經過之後,鏈接便創建成功,其後的通信數據都是以」\x00″開頭,以」\xFF」結尾。在客戶端,這個是透明的,WebSocket組件會自動將原始數據「掐頭去尾」。程序員
瀏覽器發出WebSocket鏈接請求,而後服務器發出迴應,而後鏈接創建成功,這個過程一般稱爲「握手」 (handshaking)。請看下面的請求和反饋信息:golang
圖1 WebSocket的request和response信息web
在請求中的"Sec-WebSocket-Key"是隨機的,對於成天跟編碼打交道的程序員,一眼就能夠看出來:這個是一個通過base64編碼後的數據。服務器端接收到這個請求以後須要把這個字符串鏈接上一個固定的字符串:算法
258EAFA5-E914-47DA-95CA-C5AB0DC85B11
即:f7cb4ezEAl6C3wRaU6JORA==
鏈接上那一串固定字符串,生成一個這樣的字符串:瀏覽器
f7cb4ezEAl6C3wRaU6JORA==258EAFA5-E914-47DA-95CA-C5AB0DC85B11
對該字符串先用 sha1安全散列算法計算出二進制的值,而後用base64對其進行編碼,便可以獲得握手後的字符串:安全
rE91AJhfC+6JdVcVXOGJEADEJdQ=
將之做爲響應頭Sec-WebSocket-Accept
的值反饋給客戶端。
Go語言標準包裏面沒有提供對WebSocket的支持,可是在由官方維護的go.net子包中有對這個的支持,你能夠經過以下的命令獲取該包:
go get golang.org/x/net/websocket
WebSocket分爲客戶端和服務端,接下來咱們將實現一個簡單的例子:用戶輸入信息,客戶端經過WebSocket將信息發送給服務器端,服務器端收到信息以後主動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函數創建了一個與服務器的鏈接sock,當握手成功後,會觸發WebScoket對象的onopen事件,告訴客戶端鏈接已經成功創建。客戶端一共綁定了四個事件。
咱們服務器端的實現以下:
1 package main 2 3 import ( 4 "golang.org/x/net/websocket" 5 "fmt" 6 "log" 7 "net/http" 8 ) 9 10 func Echo(ws *websocket.Conn) { 11 var err error 12 13 for { 14 var reply string 15 16 if err = websocket.Message.Receive(ws, &reply); err != nil { 17 fmt.Println("Can't receive") 18 break 19 } 20 21 fmt.Println("Received back from client: " + reply) 22 23 msg := "Received: " + reply 24 fmt.Println("Sending to client: " + msg) 25 26 if err = websocket.Message.Send(ws, msg); err != nil { 27 fmt.Println("Can't send") 28 break 29 } 30 } 31 } 32 33 func main() { 34 http.Handle("/", websocket.Handler(Echo)) 35 36 if err := http.ListenAndServe(":1234", nil); err != nil { 37 log.Fatal("ListenAndServe:", err) 38 } 39 }
當客戶端將用戶輸入的信息Send以後,服務器端經過Receive接收到了相應信息,而後經過Send發送了應答信息。
圖2 WebSocket服務器端接收到的信息
經過上面的例子咱們看到客戶端和服務器端實現WebSocket很是的方便,Go的源碼net分支中已經實現了這個的協議,咱們能夠直接拿來用,目前隨着HTML5的發展,我想將來WebSocket會是Web開發的一個重點,咱們須要儲備這方面的知識。
轉載於https://astaxie.gitbooks.io/build-web-application-with-golang/zh/08.2.html