websocket 建立過程
客戶端請求
- Connection: Upgrade 表示要升級協議
- Upgrade: websocket 告訴服務器要升級爲 websocket 協議
- Sec-WebSocket-Version: 13 表示 websocket 的版本。如果服務端不支持該版本,需要返回一個Sec-WebSocket-Versionheader,裏面包含服務端支持的版本號。
- Sec-WebSocket-Key:客戶端隨機生成的一個base64編碼,與後面服務端響應頭配套,提供基本的防護,防止無意的連接。
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com
服務器響應
- 服務器返回 101 狀態碼,並切換爲 WebSocket 協議建立全雙工連接
- Sec-WebSocket-Accept:客戶端請求頭的 Sec-Websocket-Key 與 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 拼接,通過 SHA1 算摘要,最後轉爲 base64 字符串返回。
所謂防止無意的連接是指,http 客戶端不小心請求了 websocket 服務。
HTTP/1.1 101
Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
數據幀分析
圖來自掘金-黃Java-【譯】WebSocket協議第五章——數據幀(Data Framing)
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 ... |
+---------------------------------------------------------------+
FIN: 1 bit。表示這是消息的最後一個片段。第一個片段也有可能是最後一個片段。
RSV1,RSV2,RSV3: 每個1 bit。必須設置爲0,除非擴展了非0值含義的擴展。如果收到了一個非0值但是沒有擴展任何非0值的含義,接收終端必須斷開WebSocket連接。
Opcode: 4 bit。操作碼,如果收到一個未知的操作碼,接收終端必須斷開WebSocket連接。
- %x0 表示一個持續幀
- %x1 表示一個文本幀
- %x2 表示一個二進制幀
- %x3-7 預留給以後的非控制幀
- %x8 表示一個連接關閉包
- %x9 表示一個ping包
- %xA 表示一個pong包
- %xB-F 預留給以後的控制幀
Mask: 1 bit,mask標誌位,定義“有效負載數據”是否添加掩碼。如果設置爲1,那麼掩碼的鍵值存在於Masking-Key中。Masking-Key中一般用於解碼“有效負載數據”。
Payload length: 7 bits, 7+16 bits, or 7+64 bits,以字節爲單位的“有效負載數據”長度。如果值爲0-125,那麼就表示負載數據的長度。如果是126,那麼接下來的2個bytes解釋爲16bit的無符號整形作爲負載數據的長度。如果是127,那麼接下來的8個bytes解釋爲一個64bit的無符號整形(最高位的bit必須爲0)作爲負載數據的長度。多字節長度量以網絡字節順序表示(譯註:應該是指大端序和小端序)。在所有的示例中,長度值必須使用最小字節數來進行編碼,例如:長度爲124字節的字符串不可用使用序列126,0,124進行編碼。有效負載長度是指“擴展數據”+“應用數據”的長度。“擴展數據”的長度可能爲0,那麼有效負載長度就是“應用數據”的長度。
Masking-Key: 0 or 4 bytes,所有從客戶端發往服務端的數據幀都已經與一個包含在這一幀中的32 bit的掩碼進行過了運算。爲什麼需要掩碼?爲了安全,但並不是爲了防止數據泄密,而是爲了防止早期版本的協議中存在的代理緩存污染攻擊(proxy cache poisoning attacks)等問題。
Payload data:有效負載數據,是指“擴展數據”和“應用數據”。
Extension data: x bytes:除非協商過擴展,否則“擴展數據”長度爲0 bytes。
Application data: y bytes。任意的“應用數據”,佔用“擴展數據”後面的剩餘所有字段。“應用數據”的長度等於有效負載長度減去“擴展應用”長度。