WebSocket 協議詳解
1. WebSocket 協議的幀數據詳解
1.1 幀結構
0 1 2 30 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 ... |+---------------------------------------------------------------+
WebSocket 客戶端與服務器通信的最小單位是幀(frame),一條完整消息由一個或多個幀組成:
- 發送方將消息切割為多個幀發送
- 接收方接收到消息幀后重新組裝為完整消息
- 接收方根據 FIN 標識判斷是否接收到消息的最后一個數據幀
幀基本結構
幀由多個字段順序排列構成,字段說明如下:
字段 | 長度 | 描述 |
---|---|---|
FIN | 1 位 | 表示該幀是否為消息的最后一幀1 :最后一幀,0 :還有后續幀 |
RSV1 - RSV3 | 各 1 位 | 保留位,默認值為 0 。需在握手階段協商使用 |
Opcode | 4 位 | 定義幀的操作類型(文本/二進制/控制幀等) |
Mask | 1 位 | 指示負載數據是否經過掩碼處理 客戶端發送必須掩碼,服務器發送不需掩碼 |
Payload length | 7 位/7+16位/7+64位 | 負載數據長度編碼0-125 :直接使用7位126 :后接2字節表示長度127 :后接8字節表示長度 |
Masking-key | 0 或 4 字節 | 當 Mask=1 時存在,用于負載數據掩碼處理 |
Payload data | 變長 | 實際傳輸的數據 |
字段詳細解釋
-
FIN 字段
- 標識當前幀是否為消息的最后一幀
1
:消息結束幀;0
:消息還有后續幀
-
RSV1-RSV3 字段
- 保留位,為協議擴展預留
- 默認值必須為
0
,否則接收方應斷開連接
-
Opcode 字段
值 類型 描述 0x0 延續幀 繼續未完成的消息 0x1 文本幀 UTF-8 編碼文本 0x2 二進制幀 二進制數據 0x3-0x7 保留幀 未來非控制幀擴展 0x8 關閉幀 關閉連接 0x9 Ping幀 心跳檢測 0xA Pong幀 對Ping的響應 0xB-0xF 保留幀 未來控制幀擴展 -
Mask 字段
- 客戶端發送必須設為
1
(掩碼處理) - 服務器發送必須設為
0
(無掩碼)
- 客戶端發送必須設為
-
Payload length 字段
- 采用可變長度編碼適應不同數據量
- 長度在0-125字節時直接使用7位表示
-
Masking-key 字段
- 當 Mask=1 時存在(4字節隨機密鑰)
- 掩碼算法:
C[i] = P[i] ^ M[i % 4]
- 接收方使用相同密鑰進行解掩碼
示例:文本幀 "Hello"FIN: 1 (單幀消息) RSV: 000 Opcode: 0001 (文本幀) Mask: 1 (客戶端發送) Payload length: 5 (0000101) Masking-key: 0x12345678 Payload data: "Hello" 掩碼后值
1.2 生成數據幀
消息分片機制
目的:
- 支持傳輸未知長度的超大數據
- 實現流式傳輸(邊生成邊發送)
- 避免大數據一次性加載到內存
分片規則
幀類型 | FIN | Opcode | 描述 |
---|---|---|---|
起始幀 | 0 | ≠0 | 消息的第一幀 |
中間幀 | 0 | 0 | 消息的中間部分 |
結束幀 | 1 | 0 | 消息的最后一幀 |
重要規則:
- 控制幀不允許分片(包括關閉幀/Ping/Pong)
- 控制幀可穿插在分片消息中傳輸
- 組成消息的所有幀必須是相同數據類型
- 消息碎片類型只能是文本或二進制
2. WebSocket 協議控制幀結構詳解
控制幀操作碼:0x08
(關閉)、0x09
(Ping)、0x0A
(Pong)
重要特性:
- 所有控制幀負載長度 ≤125 字節
- 控制幀禁止分片處理
- 控制幀可穿插在消息片之間傳輸
2.1 關閉幀(Opcode 0x08)
功能:正常關閉連接或指示關閉原因
幀結構要求:
- 客戶端發送必須掩碼處理
- 數據部分(若存在)前2字節為無符號整數(狀態碼)
- 可選UTF-8編碼的關閉原因(人類不可讀)
關閉流程
- 主動關閉方:發送關閉幀 → 不再發送任何數據
- 被動接收方:收到關閉幀后必須響應關閉幀
- 雙方關閉:交換關閉幀后關閉TCP連接
- 超時處理:服務器應立即關閉TCP連接;客戶端可等待或超時關閉
示例:正常關閉(狀態碼1000)FIN: 1
RSV: 000
Opcode: 1000 (關閉幀)
Mask: 1 (客戶端發送)
Payload length: 2
Payload data: 0x03E8 (1000的16進制)
2.2 Ping幀(Opcode 0x09)
功能:連接狀態檢測(心跳檢測)
重要規則:
- 可在連接建立后任意時間發送
- 必須包含應用數據(最多125字節)
- 接收方收到后必須返回Pong幀(除非已收到關閉幀)
- 響應時間:盡快返回Pong幀
示例:帶自定義數據的PingFIN: 1
RSV: 000
Opcode: 1001 (Ping幀)
Mask: 1 (客戶端發送)
Payload length: 9
Payload data: "HEARTBEAT"
2.3 Pong幀(Opcode 0x0A)
功能:對Ping幀的響應
重要規則:
- 必須包含與對應Ping完全相同的應用數據
- 對于連續多個Ping,只需響應最后一個
- 可主動發送(作為單向心跳)
- 對主動發送的Pong幀不需要響應
高級行為
- 數據一致性:必須完全復制Ping的Payload數據
- 延遲響應:可在處理完當前消息后發送
- 流控機制:不可用于流量控制
3. WebSocket 心跳機制
機制核心
關鍵規則
- 觸發條件:任何一端收到Ping幀
- 響應要求:必須立即返回Pong幀(相同Payload數據)
- 唯一例外:當連接正處于關閉狀態時可不響應
- Pong優先級:高于普通數據幀處理
應用場景
- 連接保活:防止中間設備(NAT/防火墻)斷開空閑連接
- 狀態檢測:確認對方是否在線/響應
- 網絡診斷:通過計算Ping-Pong延時(RTT)測量網絡質量
- 雙向驗證:確保連接雙向通信能力
高級實現技巧
- Payload設計:包含時間戳(計算RTT)和序列號(匹配請求響應)
- 超時機制:Ping發送方應實現響應超時檢測
- 頻率控制:推薦間隔15-60秒(視網絡環境調整)
- 錯誤處理:連續多次超時后標記連接不可用