Files
build-web-application-with-…/zh-tw/08.2.md
2019-02-26 01:40:54 +08:00

157 lines
5.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 8.2 WebSocket
WebSocket是HTML5的重要特性它實現了基於瀏覽器的遠端socket它使瀏覽器和伺服器可以進行全雙工通訊許多瀏覽器Firefox、Google Chrome和Safari都已對此做了支援。
在WebSocket出現之前為了實現即時通訊採用的技術都是“輪詢”即在特定的時間間隔內由瀏覽器對伺服器發出HTTP Request伺服器在收到請求後返回最新的資料給瀏覽器重新整理“輪詢”使得瀏覽器需要對伺服器不斷髮出請求這樣會佔用大量頻寬。
WebSocket採用了一些特殊的報頭使得瀏覽器和伺服器只需要做一個握手的動作就可以在瀏覽器和伺服器之間建立一條連線通道。且此連線會保持在活動狀態你可以使用JavaScript來向連線寫入或從中接收資料就像在使用一個常規的TCP Socket一樣。它解決了Web即時化的問題相比傳統HTTP有如下好處
- 一個Web客戶端只建立一個TCP連線
- Websocket伺服器端可以推送(push)資料到web客戶端.
- 有更加輕量級的頭,減少資料傳送量
WebSocket URL的起始輸入是ws://或是wss://在SSL上。下圖展示了WebSocket的通訊過程一個帶有特定報頭的HTTP握手被髮送到了伺服器端接著在伺服器端或是客戶端就可以透過JavaScript來使用某種套介面socket這一套介面可被用來透過事件控制代碼非同步地接收資料。
![](images/8.2.websocket.png?raw=true)
圖8.2 WebSocket原理圖
## WebSocket原理
WebSocket的協議頗為簡單在第一次handshake透過以後連線便建立成功其後的通訊資料都是以”\x00″開頭以”\xFF”結尾。在客戶端這個是透明的WebSocket元件會自動將原始資料“掐頭去尾”。
瀏覽器發出WebSocket連線請求然後伺服器發出迴應然後連線建立成功這個過程通常稱為“握手” (handshaking)。請看下面的請求和反饋資訊:
![](images/8.2.websocket2.png?raw=true)
圖8.3 WebSocket的request和response資訊
在請求中的"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語言標準套件裡面沒有提供對WebSocket的支援但是在由官方維護的go.net子套件中有對這個的支援你可以透過如下的命令取得該套件
go get golang.org/x/net/websocket
WebSocket分為客戶端和伺服器端接下來我們將實現一個簡單的例子:使用者輸入資訊客戶端透過WebSocket將資訊傳送給伺服器端伺服器端收到資訊之後主動Push資訊到客戶端然後客戶端將輸出其收到的資訊客戶端的程式碼如下
```html
<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事件告訴客戶端連線已經成功建立。客戶端一共綁定了四個事件。
- 1onopen 建立連線後觸發
- 2onmessage 收到訊息後觸發
- 3onerror 發生錯誤時觸發
- 4onclose 關閉連線時觸發
我們伺服器端的實現如下:
```Go
package main
import (
"golang.org/x/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)
}
}
```
當客戶端將使用者輸入的資訊Send之後伺服器端透過Receive接收到了相應資訊然後透過Send傳送了應答資訊。
![](images/8.2.websocket3.png?raw=true)
圖8.4 WebSocket伺服器端接收到的資訊
透過上面的例子我們看到客戶端和伺服器端實現WebSocket非常的方便Go的原始碼net分支中已經實現了這個的協議我們可以直接拿來用目前隨著HTML5的發展我想未來WebSocket會是Web開發的一個重點我們需要儲備這方面的知識。
## links
* [目錄](<preface.md>)
* 上一節: [Socket程式設計](<08.1.md>)
* 下一節: [REST](<08.3.md>)