內容不斷更新,目前包括協議中握手和數據幀的分析
1.1 背景
1.2 協議概覽
協議包含兩部分:握手,數據傳輸。
客戶端的握手如下:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
服務端的握手如下:
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= Sec-WebSocket-Protocol: chat
客戶端和服務端都發送了握手,并且成功,數據傳輸即可開始。
1.3 發起握手
發起握手是為了兼容基于HTTP的服務端程序,這樣一個端口可以同時處理HTTP客戶端和WebSocket客戶端
因此WebSocket客戶端握手是一個HTTP Upgrade請求:
GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Origin: http://example.com Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13
握手中的域的順序是任意的。
5 數據幀
5.1 概述
WebScoket協議中,數據以幀序列的形式傳輸。
考慮到數據安全性,客戶端向服務器傳輸的數據幀必須進行掩碼處理。服務器若接收到未經過掩碼處理的數據幀,則必須主動關閉連接。
服務器向客戶端傳輸的數據幀一定不能進行掩碼處理。客戶端若接收到經過掩碼處理的數據幀,則必須主動關閉連接。
針對上情況,發現錯誤的一方可向對方發送close幀(狀態碼是1002,表示協議錯誤),以關閉連接。
5.2 幀協議
WebSocket數據幀結構如下圖所示:
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-------+-+-------------+-------------------------------+ |F|R|R|R| opcode|M| Payload len | Extended payload length | |I|S|S|S| (4) |A| (7) | (16/64) | |N|V|V|V| |S| | (if payload len==126/127) | | |1|2|3| |K| | | +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + | Extended payload length continued, if payload len == 127 | + - - - - - - - - - - - - - - - +-------------------------------+ | |Masking-key, if MASK set to 1 | +-------------------------------+-------------------------------+ | Masking-key (continued) | Payload Data | +-------------------------------- - - - - - - - - - - - - - - - + : Payload Data continued ... : + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | Payload Data continued ... | +---------------------------------------------------------------+
FIN:1位
表示這是消息的最后一幀(結束幀),一個消息由一個或多個數據幀構成。若消息由一幀構成,起始幀即結束幀。
RSV1,RSV2,RSV3:各1位
MUST be 0 unless an extension is negotiated that defines meanings for non-zero values. If a nonzero value is received and none of the negotiated extensions defines the meaning of such a nonzero value, the receiving endpoint MUST _Fail the WebSocket Connection_.
這里我翻譯不好,大致意思是如果未定義擴展,各位是0;如果定義了擴展,即為非0值。如果接收的幀此處非0,擴展中卻沒有該值的定義,那么關閉連接。
OPCODE:4位
解釋PayloadData,如果接收到未知的opcode,接收端必須關閉連接。
0x0表示附加數據幀
0x1表示文本數據幀
0x2表示二進制數據幀
0x3-7暫時無定義,為以后的非控制幀保留
0x8表示連接關閉
0x9表示ping
0xA表示pong
0xB-F暫時無定義,為以后的控制幀保留
MASK:1位
用于標識PayloadData是否經過掩碼處理。如果是1,Masking-key域的數據即是掩碼密鑰,用于解碼PayloadData。客戶端發出的數據幀需要進行掩碼處理,所以此位是1。
Payload length:7位,7+16位,7+64位
PayloadData的長度(以字節為單位)。
如果其值在0-125,則是payload的真實長度。
如果值是126,則后面2個字節形成的16位無符號整型數的值是payload的真實長度。注意,網絡字節序,需要轉換。
如果值是127,則后面8個字節形成的64位無符號整型數的值是payload的真實長度。注意,網絡字節序,需要轉換。
長度表示遵循一個原則,用最少的字節表示長度(我理解是盡量減少不必要的傳輸)。舉例說,payload真實長度是124,在0-125之間,必須用前7位表示;不允許長度1是126或127,然后長度2是124,這樣違反原則。
Payload長度是ExtensionData長度與ApplicationData長度之和。ExtensionData長度可能是0,這種情況下,Payload長度即是ApplicationData長度。
WebSocket協議規定數據通過幀序列傳輸。
客戶端必須對其發送到服務器的所有幀進行掩碼處理。
服務器一旦收到無掩碼幀,將關閉連接。服務器可能發送一個狀態碼是1002(表示協議錯誤)的Close幀。
而服務器發送客戶端的數據幀不做掩碼處理,一旦客戶端發現經過掩碼處理的幀,將關閉連接。客戶端可能使用狀態碼1002。
消息分片
分片目的是發送長度未知的消息。如果不分片發送,即一幀,就需要緩存整個消息,計算其長度,構建frame并發送;使用分片的話,可使用一個大小合適的buffer,用消息內容填充buffer,填滿即發送出去。
分片規則:
1.一個未分片的消息只有一幀(FIN為1,opcode非0)
2.一個分片的消息由起始幀(FIN為0,opcode非0),若干(0個或多個)幀(FIN為0,opcode為0),結束幀(FIN為1,opcode為0)。
3.控制幀可以出現在分片消息中間,但控制幀本身不允許分片。
4.分片消息必須按次序逐幀發送。
5.如果未協商擴展的情況下,兩個分片消息的幀之間不允許交錯。
6.能夠處理存在于分片消息幀之間的控制幀
7.發送端為非控制消息構建長度任意的分片
8.client和server兼容接收分片消息與非分片消息
9.控制幀不允許分片,中間媒介不允許改變分片結構(即為控制幀分片)
10.如果使用保留位,中間媒介不知道其值表示的含義,那么中間媒介不允許改變消息的分片結構
11.如果協商擴展,中間媒介不知道,那么中間媒介不允許改變消息的分片結構,同樣地,如果中間媒介不了解一個連接的握手信息,也不允許改變該連接的消息的分片結構
12.由于上述規則,一個消息的所有分片是同一數據類型(由第一個分片的opcode定義)的數據。因為控制幀不允許分片,所以一個消息的所有分片的數據類型是文本、二進制、opcode保留類型中的一種。
需要注意的是,如果控制幀不允許夾雜在一個消息的分片之間,延遲會較大,比如說當前正在傳輸一個較大的消息,此時的ping必須等待消息傳輸完成,才能發送出去,會導致較大的延遲。為了避免類似問題,需要允許控制幀夾雜在消息分片之間。
控制幀
根據官方文檔整理,官方文檔參考http://datatracker.ietf.org/doc/rfc6455/?include_text=1