websocket協議詳解及報文分析

websocket 協議詳解及報文分析

前言

相較於HTTP協議,HTTP協議有一個的缺陷爲:通信只能由客戶端發起。在一些場景下,這種單向請求的特點,註定瞭如果服務器有連續的狀態變化,客戶端要獲知就非常麻煩。我們只能使用輪詢:每隔一段時候,就發出一個詢問,瞭解服務器有沒有新的信息。最典型的場景就是聊天室。

輪詢的效率低,非常浪費資源(因爲必須不停連接,或者 HTTP 連接始終打開)。因此,工程師們一直在思考,有沒有更好的方法。WebSocket 就是這樣發明的。

websocket 協議概述

Webscoket是Web瀏覽器和服務器之間的一種全雙工通信協議,其中WebSocket協議由IETF定爲標準,WebSocket API由W3C定爲標準。一旦Web客戶端與服務器建立起連接,之後的全部數據通信都通過這個連接進行。通信過程中,可互相發送JSON、XML、HTML或圖片等任意格式的數據。
WS(WebSocket)與HTTP協議相比,

相同點主要有:

  • 都是基於TCP的應用層協議;
  • 都使用Request/Response模型進行連接的建立;
  • 在連接的建立過程中對錯誤的處理方式相同,在這個階段WS可能返回和HTTP相同的返回碼;
  • 都可以在網絡中傳輸數據。

不同之處在於:

  • WS使用HTTP來建立連接,但是定義了一系列新的header域,這些域在HTTP中並不會使用;
  • WS的連接不能通過中間人來轉發,它必須是一個直接連接;
  • WS連接建立之後,通信雙方都可以在任何時刻向另一方發送數據;
  • WS連接建立之後,數據的傳輸使用幀來傳遞,不再需要Request消息;
  • WS的數據幀有序。

websocket 通信過程及對應報文分析

WS整個通信過程如下圖所示:

websocket是基於TCP的一個應用協議,與HTTP協議的關聯之處在於websocket的握手數據被HTTP服務器當作HTTP包來處理,主要通過Update request HTTP包建立起連接,之後的通信全部使用websocket自己的協議。

**請求:**TCP連接建立後,客戶端發送websocket的握手請求,請求報文頭部如下:

GET /uin=xxxxxxxx&app=xxxxxxxxx&token=XXXXXXXXXXXX HTTP/1.1
Host: server.example.cn:443
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36
Upgrade: websocket
Sec-WebSocket-Version: 13
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: user_id=XXXXX
Sec-WebSocket-Key: 1/2hTi/+eNURiekpNI4k5Q==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Sec-WebSocket-Protocol: binary, base64
  • 第一行爲爲請求的方法,類型必須爲GET,協議版本號必須大於1.1
  • Upgrade字段必須包含,值爲websocket
  • Connection字段必須包含,值爲Upgrade
  • Sec-WebSocket-Key字段必須包含 ,記錄着握手過程中必不可少的鍵值。
  • Sec-WebSocket-Protocol字段必須包含 ,記錄着使用的子協議
  • Origin(請求頭):Origin用來指明請求的來源,Origin頭部主要用於保護Websocket服務器免受非授權的跨域腳本調用Websocket API的請求。也就是不想沒被授權的跨域訪問與服務器建立連接,服務器可以通過這個字段來判斷來源的域並有選擇的拒絕。

**響應:**服務器接收到請求後,返回狀態碼爲101 Switching Protocols 的響應。

HTTP/1.1 101 Switching Protocols
Server: WebSockify Python/2.6.6
Date: Wed, 27 May 2020 03:03:21 GMT
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: hXXXXXXXXXXXXXXxGmM=
Sec-WebSocket-Protocol: binary

Sec-WebSocket-Accept字段是由握手請求中的Sec-WebSocket-Key字段生層的。

握手成功後,通信不再使用HTTP協議,而採用WebSocket獨立的數據幀。如下圖所示,爲協議幀格式:

FIN,指明Frame是否是一個Message裏最後Frame(之前說過一個Message可能又多個Frame組成);1bit,是否爲信息的最後一幀
RSV1-3,默認是0 (必須是0),除非有擴展定義了非零值的意義。
Opcode,這個比較重要,有如下取值是被協議定義的
				0x00 denotes a continuation frame
				0x01 表示一個text frame
				0x02 表示一個binary frame
				0x03 ~~ 0x07 are reserved for further non-control frames,爲將來的非控制消息片段保留測操作碼
				0x08 表示連接關閉
				0x09 表示 ping (心跳檢測相關)
				0x0a 表示 pong (心跳檢測相關)
				0x0b ~~ 0x0f are reserved for further control frames,爲將來的控制消息片段保留的操作碼
Mask,這個是指明“payload data”是否被計算掩碼。這個和後面的Masking-key有關,如果設置爲1,掩碼鍵必須放在masking-key區域,客戶端發送給服務端的所有消息,此位的值都是1;
Payload len,數據的長度,
Masking-key,0或者4bit,只有當MASK設置爲1時纔有效。,給一個Websocket中掩碼的意義
Payload data,幀真正要發送的數據,可以是任意長度,但儘管理論上幀的大小沒有限制,但發送的數據不能太大,否則會導致無法高效利用網絡帶寬,正如上面所說Websocket提供分片。
Extension data:擴展數據,如果客戶端和服務端沒有特殊的約定,那麼擴展數據長度始終爲0
Application data:應用數據,

websocket 報文細節,這裏由於client 和 server 端的 ip 都是127.0.0.1 :

nginx 支持websocket 配置

由於http 請求 涉及 反向代理 所以就涉及 nginx 配置需要支持 websocket 需要做一些特殊的配置;

# 配置Nginx支持webSocket開始
proxy_set_header Host $http_host;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

其他通過代碼模擬websocket 的代碼可以查閱其他博客內容,這裏就不贅述;

https://www.tutorialspoint.com/websockets/websockets_send_receive_messages.htm

http://www.ruanyifeng.com/blog/2017/05/websocket.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章