網絡協議之:WebSocket的消息格式


簡介

我們知道WebSocket是建立在TCP協議基礎上的一種網絡協議,用來進行客戶端和服務器端的實時通信。非常的好用。最簡單的使用WebSocket的辦法就是直接使用瀏覽器的API和服務器端進行通信。

本文將會深入分析WebSocket的消息交互格式,讓大家得以明白,websocket到底是怎么工作的。

WebSocket的握手流程

我們知道WebSocket為了兼容HTTP協議,是在HTTP協議的基礎之上進行升級得到的。在客戶端和服務器端建立HTTP連接之后,客戶端會向服務器端發送一個升級到webSocket的協議,如下所示:

GET /chat HTTP/1.1
Host: example.com:8000
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

注意,這里的HTTP版本必須是1.1以上。HTTP的請求方法必須是GET

通過設置Upgrade和Connection這兩個header,表示我們准備升級到webSocket了。

除了這里列的屬性之外,其他的HTTP自帶的header屬性都是可以接受的。

這里還有兩個比較特別的header,他們是Sec-WebSocket-Version和Sec-WebSocket-Key。

先看一下Sec-WebSocket-Version, 它表示的是客戶端請求的WebSocket的版本號。如果服務器端並不明白客戶端發送的請求,則會返回一個400 ("Bad Request"),在這個返回中,服務器端會返回失敗的信息。

如果是不懂客戶端發送的Sec-WebSocket-Version,服務器端同樣會將Sec-WebSocket-Version返回,以告知客戶端。

這里要特別關注的一個header字段就是Sec-WebSocket-Key。我們接下來看一下這個字段到底有什么用。

當服務器端收到客戶端的請求之后,會返回給客戶端一個響應,告訴客戶端協議已經從HTTP升級到WebSocket了。

返回的響應可能是這樣的:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

這里的Sec-WebSocket-Accept是根據客戶端請求中的Sec-WebSocket-Key來生成的。具體而言是將客戶端發送的Sec-WebSocket-Key 和 字符串"258EAFA5-E914-47DA-95CA-C5AB0DC85B11" 進行連接。然后使用SHA1算法求得其hash值。

最后將hash值進行base64編碼即可。

當服務器端返回Sec-WebSocket-Accept之后,客戶端可以對其進行校驗,已完成整個握手過程。

webSocket的消息格式

之所以要使用webSocket是因為client和server可以隨時隨地發送消息。這是websocket的神奇所在。那么發送的消息是什么格式的呢?我們來詳細看一下。

client和server端進行溝通的消息是以一個個的frame的形式來傳輸的。frame的格式如下:


      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 ...                |
     +---------------------------------------------------------------+

MASK表示的是消息是否是被編碼過的,對於從client過來的消息來說,MASK必須是1。如果client發送給server端的消息,MASK不為1,則server需要斷開和client的連接。但是server端發送給client端的消息,MASK字段就不需要設置了。

RSV1-3是擴展的字段,可以忽略。

opcode表示怎么去解釋payload字段。payload就是實際要傳遞的消息。0x0表示繼續,0x1表示文本,0x2表示二進制,其他的表示控制字段。

FIN表示是否是消息的最后一個frame。如果是0,表示該消息還有更多的frame。如果是1表示,該frame是消息的最后一部分了,可以對消息進行處理了。

為什么需要Payload len字段呢?因為我們需要知道什么時候停止接收消息。所以需要一個表示payload的字段來對消息進行具體的處理。

怎么解析Payload呢?這個就比較復雜。

  1. 首先讀取9-15 bits,將其解析為無符號整數。如果其小於125,那么這個就是payload的長度,結束。如果是126,那么就去到第二步。如果是127,那么就去到第三步。
  2. 讀取下一個16 bits,然后將其解析為無符號整數,結束。
  3. 讀取下一個64 bits。將其解析為符號整數。結束。

如果設置了Mask,那么讀取下4個字節,也就是32bits。這個是masking key。當數據讀取完畢之后,我們就獲取到了編碼過后的payload:ENCODED,和MASK key。要解碼的話,其邏輯如下:

var DECODED = "";
for (var i = 0; i < ENCODED.length; i++) {
    DECODED[i] = ENCODED[i] ^ MASK[i % 4];

FIN可以和opcode一起配合使用,用來發送長消息。

FIN=1表示,是最后一個消息。 0x1表示是text消息,0x2是0,表示是二凈值消息,0x0表示消息還沒有結束,所以0x0通常和FIN=0 一起使用。

Extensions和Subprotocols

在客戶端和服務器端進行握手的過程中,在標准的websocket協議基礎之上,客戶端還可以發送Extensions或者Subprotocols。這兩個有什么區別呢?

首先這兩個都是通過HTTP頭來設置的。但是兩者還是有很大的不同。Extensions可以對WebSocket進行控制,並且修改payload,而subprotocols只是定義了payload的結構,並不會對其進行修改。

Extensions是可選的,而Subprotocols是必須的。

你可以將Extensions看做是數據壓縮,它是在webSocket的基礎之上,對數據進行壓縮或者優化操作,可以讓發送的消息更短。

而Subprotocols 表示的是消息的格式,比如使用soap或者wamp。

子協議是在WebSocket協議基礎上發展出來的協議,主要用於具體的場景的處理,它是是在WebSocket協議之上,建立的更加嚴格的規范。

比如,客戶端請求服務器時候,會將對應的協議放在Sec-WebSocket-Protocol頭中:

GET /socket HTTP/1.1
...
Sec-WebSocket-Protocol: soap, wamp

服務器端會根據支持的類型,做對應的返回,如:

Sec-WebSocket-Protocol: soap

總結

本文講解了webSocket消息交互的具體格式,可以看到很多強大功能的協議,都是由最最基本的結構組成的。

本文已收錄於 http://www.flydean.com/07-websocket-message/

最通俗的解讀,最深刻的干貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!

歡迎關注我的公眾號:「程序那些事」,懂技術,更懂你!


免責聲明!

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



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