websocket報文格式及掩碼處理方式


1、數據幀格式概覽

下面給出了WebSocket數據幀的統一格式。熟悉TCP/IP協議的同學對這樣的圖應該不陌生。

  1. 從左到右,單位是比特。比如FIN、RSV1各占據1比特,opcode占據4比特。
  2. 內容包括了標識、操作代碼、掩碼、數據、數據長度等。(下一小節會展開)
 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 ...                |
 +---------------------------------------------------------------+
2、數據幀格式詳解

針對前面的格式概覽圖,這里逐個字段進行講解,如有不清楚之處,可參考協議規范,或留言交流。

FIN:1個比特。

如果是1,表示這是消息(message)的最后一個分片(fragment),如果是0,表示不是是消息(message)的最后一個分片(fragment)。

RSV1, RSV2, RSV3:各占1個比特。

一般情況下全為0。當客戶端、服務端協商采用WebSocket擴展時,這三個標志位可以非0,且值的含義由擴展進行定義。如果出現非零的值,且並沒有采用WebSocket擴展,連接出錯。

Opcode: 4個比特。

操作代碼,Opcode的值決定了應該如何解析后續的數據載荷(data payload)。如果操作代碼是不認識的,那么接收端應該斷開連接(fail the connection)。可選的操作代碼如下:

%x0:表示一個延續幀。當Opcode為0時,表示本次數據傳輸采用了數據分片,當前收到的數據幀為其中一個數據分片。
%x1:表示這是一個文本幀(frame)
%x2:表示這是一個二進制幀(frame)
%x3-7:保留的操作代碼,用於后續定義的非控制幀。
%x8:表示連接斷開。
%x9:表示這是一個ping操作。
%xA:表示這是一個pong操作。
%xB-F:保留的操作代碼,用於后續定義的控制幀。
Mask: 1個比特。

表示是否要對數據載荷進行掩碼操作。從客戶端向服務端發送數據時,需要對數據進行掩碼操作;從服務端向客戶端發送數據時,不需要對數據進行掩碼操作。

如果服務端接收到的數據沒有進行過掩碼操作,服務端需要斷開連接。

如果Mask是1,那么在Masking-key中會定義一個掩碼鍵(masking key),並用這個掩碼鍵來對數據載荷進行反掩碼。所有客戶端發送到服務端的數據幀,Mask都是1。
掩碼的算法、用途在下一小節講解。

Payload length:

數據載荷的長度,單位是字節。為7位,或7+16位,或1+64位。

假設數Payload length === x,如果

x為0~126:數據的長度為x字節。
x為126:后續2個字節代表一個16位的無符號整數,該無符號整數的值為數據的長度。
x為127:后續8個字節代表一個64位的無符號整數(最高位為0),該無符號整數的值為數據的長度。
此外,如果payload length占用了多個字節的話,payload length的二進制表達采用網絡序(big endian,重要的位在前)。

Masking-key:0或4字節(32位)

所有從客戶端傳送到服務端的數據幀,數據載荷都進行了掩碼操作,Mask為1,且攜帶了4字節的Masking-key。如果Mask為0,則沒有Masking-key。

備注:載荷數據的長度,不包括mask key的長度。

Payload data:(x+y) 字節

載荷數據:包括了擴展數據、應用數據。其中,擴展數據x字節,應用數據y字節。

擴展數據:如果沒有協商使用擴展的話,擴展數據數據為0字節。所有的擴展都必須聲明擴展數據的長度,或者可以如何計算出擴展數據的長度。此外,擴展如何使用必須在握手階段就協商好。如果擴展數據存在,那么載荷數據長度必須將擴展數據的長度包含在內。

應用數據:任意的應用數據,在擴展數據之后(如果存在擴展數據),占據了數據幀剩余的位置。載荷數據長度 減去 擴展數據長度,就得到應用數據的長度。

3、掩碼算法

掩碼鍵(Masking-key)是由客戶端挑選出來的32位的隨機數。掩碼操作不會影響數據載荷的長度。掩碼、反掩碼操作都采用如下算法:

首先,假設:

original-octet-i:為原始數據的第i字節。
transformed-octet-i:為轉換后的數據的第i字節。
j:為i mod 4的結果。
masking-key-octet-j:為mask key第j字節。

算法描述為:
original-octet-imasking-key-octet-j異或后,得到 transformed-octet-i

j = i MOD 4
transformed-octet-i = original-octet-i XOR masking-key-octet-j
4、掩碼樣例

場景:
客戶端:發送語音文件到服務端,先發送txt消息文件名稱"tts";再讀物文件,發送bin消息二進制流數據(多幀發送)。

服務端:先接收txt消息,需要接收的文件名稱並創建打開;再接收bin消息語音流消息,並寫入文件;當接收到最后一幀二進制后,關閉打開的文件。

txt消息生成的 掩碼KEY:

the key is 14,51,172,208.

客服端處理消息:“tts” =》122,71,-33

  1. 客戶端生成原始4位掩碼
  2. 將需要發送字符與掩碼異或運算,獲取計算后的值,用於網絡傳輸。
    116(t)與掩碼14異或處理,得到異或值:122
    116(t)與掩碼51異或處理,得到異或值:71
    115(s)與掩碼172異或處理,得到異或值:223
  3. 生成待發送消息:121,71,223

服務端處理消息:122,71,223 =》 “tts”

  1. 獲取客戶端發送的4位掩碼和數據消息
  2. 將收到的字符消息與掩碼異或運算,獲取源字符值
    122與掩碼(14)異或處理,得到原字符值:116(t)
    71與掩碼(51)異或處理,得到原字符值:116(t)
    233與掩碼(172)異或處理,得到原字符值:115(s)
  3. 解碼獲取文件名:116,116,115;及字符:tts


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM