RFC-6455 The WebSocket Protocol 淺讀


什么是WebSokcet?

WebSocket是一種協議,並且是各大主流瀏覽器作為客戶端支持的協議。它的目標就是用來替代基於 XMLHTTPRequest和長輪詢的解決方案。應用在時時彈幕,消息推送,棋牌游戲等需要及時通訊的業務場景。

握手

WebSocket連接有兩個階段:握手(handshake)和數據傳輸(data transfer)。此握手非TCP三次握手,但是目的差不多,就是客戶端告訴瀏覽器我想要使用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

那么如果握手成功的話,服務器響應:

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

客戶端發送握手請求

  1. Uri要滿足如下格式:
ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ]
wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ]
  1. 在與服務端建立連接時,客戶端必須方發送握手請求,請求是一個HTTP的升級協議(Upgrade)請求。並且該請求必須滿足
    • 握手請求必須是一個正常的HTTP請求。
    • 請求方法必須為GET,並且HTTP協議最低為1.1
    • 請求頭必須包含Host
    • 請求頭必須包含Upgrade,並且值為websocket
    • 請求頭必須包含Connection,並且值為Upgrade
    • 請求頭必須包含Sec-WebSocket-Key,值為經過Base64轉換的長度為16字節的一組數據
    • 請求頭必須包含Origin,如果客戶端是瀏覽器這個值肯定是有的,如果非瀏覽器的客戶端,這個值可以隨意改。
    • 請求頭必須包含Sec-WebSocket-Version,並且值為13
    • 請求頭可以帶一個Sec-WebSocket-Protocol,這個值告訴服務端客戶端想用的子協議,多個用逗號分開
    • 請求頭可以帶一個Sec-WebSocket-Extensions,這個值告訴服務端客戶端支持的協議級別的擴展。
    • 請求頭可以帶一個和權限校驗相關的頭,例如Cookie,Authentication等

當客戶端將握手請求發出去之后,就要等待服務端的響應了。當服務端成功響應之后,客戶端還需要做如下校驗:

  1. 返回的響應碼非101,例如401,500,403,503 等等,客戶端連接失敗
  2. 返回的響應頭部不包含Upgrade或者Upgrade的值不是websocket,客戶端連接失敗
  3. 返回的響應頭部不包含Connection或者Connection的值不是Upgrade,客戶端連接失敗
  4. 返回的響應頭部不包含Sec-WebSocket-Accept或者Sec-WebSocket-Accept的值並不是Base64(SHA1(Sec-WebSocket-Key+"258EAFA5-E914-47DA-95CA-C5AB0DC85B11")),客戶端連接失敗
  5. 返回的響應頭部Sec-WebSocket-Extensions中的值並不是客戶端發送的Sec-WebSocket-Extensions中的值,客戶端連接失敗
  6. 返回的響應頭部Sec-WebSocket-Protocol中的值並不是客戶端發送的Sec-WebSocket-Protocol中的值,客戶端連接失敗

服務端接收握手請求
如果服務端在處理請求過程中不滿足一下任何一點,服務端都會終止處理該請求

  1. 必須是HTTP1.1+的GET請求
  2. 包含Host請求頭
  3. 包含Upgrade值為WebSocket的請求頭
  4. 包含Connection值為Upgrade的請求頭
  5. 包含Sec-WebSocket-Key值為16字節長度的Base64字符串
  6. 包含Sec-WebSocket-Version值為13的請求頭
  7. 非必須:Origin
  8. 非必須:Sec-WebSocket-Protocol
  9. 非必須:Sec-WebSocket-Extensions

當服務端確定這是一個正常的握手請求並且願意處理此請求,那么服務端需要回應一個HTTP響應:

  1. 狀態碼必須為 101 Switching Protocol
  2. Upgrade:WebSocket
  3. Connection:Upgrade
  4. Sec-WebSocket-Accept,如上文所說,值為:Base64(SHA1(Sec-WebSocket-Key+"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"))
  5. Sec-WebSocket-Protocol,根據客戶端傳的值
  6. Sec-WebSocket-Extensions,根據客戶端傳的值

至此,握手結束。連接狀態由CONNECTING進入OPEN狀態

協議幀

WebSocket的協議幀格式如下:
WebSocket協議

  • FIN 1bit
    包結束標志,1 代表最后一個消息包,0代表某一段消息包
  • RSV1, RSV2, RSV3: 每個1bit,共3bit
    值為0,除非協議擴展(Extensions)聲明了非0的值的含義。如果服務端收到非0的值,並且沒有相應的定義,那么服務端將直接終止連接。
  • Opcode 4bit
    x0 后續幀
    x1 文本幀
    x2 二進制幀
    x3-X7 非控制幀預留
    x8 關閉連接
    x9 PING
    xA PONG
    xB-xF 控制幀預留
  • Mask 1 bit 是否掩碼。客戶端向服務器發送,必須掩碼。服務端向客戶端發送不需掩碼
  • PayLoad Length 7bits,7+16bits,7+64bits,如果值為 0-125,則數據包長度為0-125.如果值為126,則后2個字節為數據包長度:16bit。如果值為127,則后8個字節為數據包長度:64bit。
  • Masking-Key, 0-4bits.是否有值取決於 Mask 標識位是否為1.
  • Extension data X bytes 如果在握手時協商了擴展,會有值,否則為0
  • Application data y bytes 剩余消息包
  • PayLoad data (x+y)bytes 總消息包=Extension data + Application data.如果有掩碼,解碼公式如下:
body[i] = body[i] ^ body[i % 4]

代碼解析

下面我用tio網絡通訊框架代碼來解釋一下上文中的內容,不必糾結具體代碼,只要大概理解代碼功能即可。
WebSocket協議
具體協議升級代碼如下:
WebSocket協議
以上就是握手部分Http協議升級過程的代碼部分。沒有什么難理解的地方,只要對着文檔要求去實現即可。不過要注意的是,這里是升級協議的過程,如果有其他業務處理,比如訪問權限校驗失敗等,可以直接返回 HttpStatusCode 401.

協議幀解析:
WebSocket協議
WebSocket協議

總結

大致過了一遍RFC-6455文檔,發現還是官方文檔中解釋的更詳細的也更清楚一些,但是苦於英語水平不過關,有些部分理解起來比較困難。

參考資料

RFC-6455


免責聲明!

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



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