RFC-6455 The WebSocket Protocol 淺讀

什麼是WebSokcet?

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

握手

WebSocket鏈接有兩個階段:握手(handshake)和數據傳輸(data transfer)。此握手非TCP三次握手,可是目的差很少,就是客戶端告訴瀏覽器我想要使用WebSocket協議進行通信。客戶端須要發送以下請求,它是一個 HTTP Upgrade 請求:web

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等

當客戶端將握手請求發出去以後,就要等待服務端的響應了。當服務端成功響應以後,客戶端還須要作以下校驗:websocket

  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狀態socket

協議幀

WebSocket的協議幀格式以下:
WebSocket協議3d

  • 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.code

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

總結

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

參考資料

RFC-6455

相關文章
相關標籤/搜索