Websocket協議數據幀傳輸和關閉鏈接

以前總結了關於Websocket協議的握手鍊接方式等其餘細節,如今對socket鏈接創建後的數據幀傳輸和關閉細節總結。web

1、數據幀格式

數據傳輸使用的是一系列數據幀,出於安全考慮和避免網絡截獲,客戶端發送的數據幀必須進行掩碼處理後才能發送到服務器,不管是否是在TLS安全協議上都要進行掩碼處理。服務器若是沒有收到掩碼處理的數據幀時應該關閉鏈接,發送一個1002的狀態碼。服務器不能將發送到客戶端的數據進行掩碼處理,若是客戶端收到掩碼處理的數據幀必須關閉鏈接。算法

基本的數據幀爲一個opcode、一個payload長度和發送的應用數據,根據ABNF的定義,詳細信息以下圖緩存

這裏使用的是數據存儲的位(bit),當進行加密的時候,最終要的一位就是最左邊的第一個。安全

 

  • FIN :1bit ,表示是消息的最後一幀,若是消息只有一幀那麼第一幀也就是最後一幀。
  • RSV1,RSV2,RSV3:每一個1bit,必須是0,除非擴展定義爲非零。若是接受到的是非零值可是擴展沒有定義,則須要關閉鏈接。
  • Opcode:4bit,解釋Payload數據,規定有如下不一樣的狀態,若是是未知的,接收方必須立刻關閉鏈接。狀態以下:0x0(附加數據幀)    0x1(文本數據幀)   0x2(二進制數據幀)    0x3-7(保留爲以後非控制幀使用)  0xB-F(保留爲後面的控制幀使用)    0x8(關閉鏈接幀)  0x9(ping)  0xA(pong)
  •    Mask:1bit,掩碼,定義payload數據是否進行了掩碼處理,若是是1表示進行了掩碼處理。
    Masking-key域的數據便是掩碼密鑰,用於解碼PayloadData。客戶端發出的數據幀須要進行掩碼處理,因此此位是1。
  • Payload length:7位,7 + 16位,7+64位,payload數據的長度,若是是0-125,就是真實的payload長度,若是是126,那麼接着後面的2個字節對應的16位無符號整數就是payload數據長度;若是是127,那麼接着後面的8個字節對應的64位無符號整數就是payload數據的長度。
  • Masking-key:0到4字節,若是MASK位設爲1則有4個字節的掩碼解密密鑰,不然就沒有。
  • Payload data:任意長度數據。包含有擴展定義數據和應用數據,若是沒有定義擴展則沒有此項,僅含有應用數據。

2、客戶端到服務器端掩碼處理

前面說過客戶端發送到服務器端的數據必須進行掩碼處理,掩碼的密鑰是一個32位的隨機值,客戶端隨機選取密鑰必須是不可猜想的。這個掩碼處理後並不影響Payload數據的長度。服務器收到掩碼處理後的數據後,解碼須要使用以下的算法進行:
j = i mod 4(i 是傳輸數據中的十進制的索引下標)
轉換後的數據 d = original  ^ mask[j]
也就是將Payload原始數據的每一個字符的順序下標與4去摸,而後將此原始數據字符與掩碼的前面去摸後的相應位置的字符進行異或操做便可。這個算法對於加密和解密的操做都是同樣的。

3、消息分片

分片目的是發送長度未知的消息。若是不分片發送,即一幀,就須要緩存整個消息,計算其長度,構建frame併發送;使用分片的話,可以使用一個大小合適的buffer,用消息內容填充buffer,填滿即發送出去。服務器

分片規則:websocket

1.一個未分片的消息只有一幀(FIN爲1,opcode非0)網絡

2.一個分片的消息由起始幀(FIN爲0,opcode非0),若干(0個或多個)幀(FIN爲0,opcode爲0),結束幀(FIN爲1,opcode爲0)。併發

3.控制幀能夠出如今分片消息中間,但控制幀自己不容許分片。socket

4.分片消息必須按次序逐幀發送。加密

5.若是未協商擴展的狀況下,兩個分片消息的幀之間不容許交錯。

6.可以處理存在於分片消息幀之間的控制幀

7.發送端爲非控制消息構建長度任意的分片

8.client和server兼容接收分片消息與非分片消息

9.控制幀不容許分片,中間媒介不容許改變分片結構(即爲控制幀分片)

10.若是使用保留位,中間媒介不知道其值表示的含義,那麼中間媒介不容許改變消息的分片結構

11.若是協商擴展,中間媒介不知道,那麼中間媒介不容許改變消息的分片結構,一樣地,若是中間媒介不瞭解一個鏈接的握手信息,也不容許改變該鏈接的消息的分片結構

12.因爲上述規則,一個消息的全部分片是同一數據類型(由第一個分片的opcode定義)的數據。由於控制幀不容許分片,因此一個消息的全部分片的數據類型是文本、二進制、opcode保留類型中的一種。

須要注意的是,若是控制幀不容許夾雜在一個消息的分片之間,延遲會較大,好比說當前正在傳輸一個較大的消息,此時的ping必須等待消息傳輸完成,才能發送出去,會致使較大的延遲。爲了不相似問題,須要容許控制幀夾雜在消息分片之間。

 

數據幀示例:

未掩碼處理的文本單數據幀:  0x81 0x05 0x48 0x65 0x6c 0x6c 0x6f (contains "Hello")

掩碼處理的文本單數據幀:      0x81 0x85 0x37 0xfa 0x21 0x3d 0x7f 0x9f 0x4d 0x51 0x58

分片未掩碼處理的文本消息:   0x01 0x03 0x48 0x65 0x6c (contains "Hel")

 0x80 0x02 0x6c 0x6f (contains "lo")

未掩碼處理的Ping請求和掩碼處理的響應: 

0x89 0x05 0x48 0x65 0x6c 0x6c 0x6f (contains a body of "Hello", but the contents of the body are arbitrary) 

0x8a 0x85 0x37 0xfa 0x21 0x3d 0x7f 0x9f 0x4d 0x51 0x58 (contains a body of "Hello", matching the body of the ping)

64K的二進制數據:0x82 0x7F 0x0000000000010000 [65536 bytes of binary data]

 

4、發送和接收數據

一、發送

 

  • 端點必須確保WebSocket鏈接處於OPEN狀態。若是在任什麼時候刻WebSocket鏈接的狀態改變了,端點必須終止如下步驟。
  • 端點必須封裝/data/到一個WebSocket幀。若是要發送的數據太大或若是在端點想要開始發生數據時數據做爲一個總體不可用,端點能夠交替地封裝數據到一系列的幀中。
  • 第一個包含數據的幀的操做碼(幀-opcode)必須設置爲適當的值用於接收者解釋數據是文本仍是二進制數據。
  • 包含數據的最後幀的FIN位(幀-fin)必須設置位1。
  • 若是數據正由客戶端發送,幀被掩碼。
  • 若是任何擴展已經協商用於WebSocket鏈接,額外的考慮能夠按照這些擴展定義來應用。
  • 已成形的幀必須在底層網絡鏈接之上傳輸。

 

二、接收

爲了接收WebSocket數據,端點監聽底層網絡鏈接。傳入數據必須解析爲WebSocket幀。當接收到一個數據幀時,端點必須注意由操做碼(幀-opcode)定義的數據的/type/。這個幀的「應用數據」被定義爲消息的/data/。若是幀由一個未分片的消息組成,這是說已經接收到一個WebSocket消息,其類型爲/type/且數據爲/data/。若是幀是一個分片消息的一部分,隨後數據幀的「應用數據」鏈接在一塊兒造成/data/。當接收到由FIN位(幀-fin)指示的最後的片斷時,這是說已經接收到一個WebSocket消息,其數據爲/data/(由連續片斷的「應用數據」組成)且類型爲/type/(分配消息的第一個幀指出)。隨後的數據幀必須被解釋爲屬於一個新的WebSocket消息。

擴展能夠改變數據如何讀的語義,尤爲包括什麼組成一個消息的邊界。擴展,除了在負載中的「應用數據」以前添加「擴展數據」外,也能夠修改「應用數據」(例如壓縮它)。服務器必須爲從客戶端接收到的數據幀移除掩碼。

 

5、Websocket關閉

通訊的兩端中任意一端關閉均可以關閉socket鏈接,關閉時應該清楚全部的TCP鏈接資源和TLS回話的資源,同時要丟棄全部的可能接收的字節數據。首先關閉的一方通常都應該是服務器端,而後處於TIME_WAIT狀態。

爲了使用一個狀態碼關閉websocket,一端必須發送一個關閉的控制幀,當兩端都發送了關閉數據幀時,雙方都要關閉全部的鏈接資源。控制幀爲一個「狀態碼」和一個「緣由說明」,當關閉以後,雙方處於CLOSED狀態。

相關文章
相關標籤/搜索