http通訊只能一來一回,不能直接回包,於是出現了websocket實時通訊
背景
在websocket沒有出現之前,總會有一些需求,需要獲取資料,但是這些資料卻不知道哪個時間點才能獲取到,於是,大家使用了輪詢來解決伺服器推送的問題。
HTTP協議只能從客戶端發出請求,伺服器處理返回請求(這裡可以看下圖),但是伺服器不能主動給客戶端傳送請求,於是解決伺服器推送的問題,就只能靠輪詢了。
輪詢分為兩種
-
短輪詢:定時傳送http請求
-
長輪詢:傳送請求直到收到訊息or超時後繼續傳送下一個請求
但是,輪詢並不能完美的解決伺服器推送的問題
- 伺服器無法主動傳送資料
- 輪詢實時性差
- 輪詢浪費較多資源
概念
websocket的出現,就是為了解決伺服器推送的問題。
WebSocket是HTML5開始提供的一種在單個 TCP 連線上進行全雙工(通訊雙方既是接收方也是傳送方,兩端裝置可以同時傳送和接收資料)通訊的協議。
特性
- 建立在TCP連結之上
- 較少的控制開銷
- 更強的實時性
- 保持連線狀態
- 可以傳送文字、二進位制資料
- 沒有同源限制
連結過程
websocket的連結還是比較簡單的,只要進行一次握手,就可以進行後續的通訊了,這裡分為三步:
- http get請求將http請求升級為websocket請求
- 資料通訊
- 斷開連結(可由任一方中斷)
握手協議
在第一步的時候進行了握手,這裡來看一下請求的詳情
// 請求
GET / HTTP/1.1
Upgrade: websocket // 升級為websocket協議
Connection: Upgrade
Host: localhost:3000 // 請求host
Origin: http://binnie.com // 請求來源
Sec-WebSocket-Key: sN9cRrP/n9NdMgdcy2VJFQ== // 用於後續計算accept
Sec-WebSocket-Version: 13 // websocket版本號
複製程式碼
// 回包
HTTP/1.1 101 Switching Protocols // 101表示連結成功
Upgrade: websocket // 與請求對應
Connection: Upgrade
// 使用key計算得來,儘量避免普通HTTP請求被誤認為Websocket協議
Sec-WebSocket-Accept: fFBooB7FAkLlXgRSz0BT3v4hq5s=
複製程式碼
幀格式
WebSocket協議使用幀(Frame)收發資料
客戶端傳送給服務端的幀必須通過4位元組的掩碼(Masking-key)加密,服務端收到訊息後,用掩碼對資料幀的Payload Data進行異或運算解碼得到資料,如果服務端收到未經掩碼加密的資料幀,則應該馬上關閉該WebSocket。
服務端發給客戶端的資料則不需要掩碼加密,客戶端如果收到了服務端的掩碼加密的資料,則也必須關閉它。
上圖為websocket的幀格式,下面我們一個個來分析
這裡從左往右看,橫向為32位(bit)
- FIN:最後的片段
- RSV1-3:擴充定義
- opcode:幀型別,其中控制幀:0x8 (Close), 0x9 (Ping), and 0xA (Pong),資料幀主要有:0x1 (Text), 0x2 (Binary)
- MASK:資料是否掩碼
- Payload len:負載長度
- Extended payload length:擴充負載長度
- Masking-key:掩碼
- Playload Data:負載資料
所以,websocket傳遞資料時最小的請求頭為 FIN+RSV1-3+opcode+MASK+Payload len+Masking-key = 6 byte(位元組),剩下的就都是實際資料,相比於http頭部小得多。
相容性
websocket是很好的實時雙向通訊,不過相容性只到ie10,所以不支援websocket的瀏覽器就只能使用輪詢等方法來相容。
寫在最後
由於低版本瀏覽器不支援websocket,需要使用輪詢來相容,於是出現了很多websocket庫,後面再分析websocket庫的實現。