1. 程式人生 > >WebSocket 通訊過程與實現

WebSocket 通訊過程與實現

(點選上方公眾號,可快速關注)

來源:wzhvictor

segmentfault.com/a/1190000014643900

什麼是 WebSocket ?

WebSocket 是一種標準協議,用於在客戶端和服務端之間進行雙向資料傳輸。但它跟 HTTP 沒什麼關係,它是基於 TCP 的一種獨立實現。

以前客戶端想知道服務端的處理進度,要不停地使用 Ajax 進行輪詢,讓瀏覽器隔個幾秒就向伺服器發一次請求,這對伺服器壓力較大。另外一種輪詢就是採用 long poll 的方式,這就跟打電話差不多,沒收到訊息就一直不掛電話,也就是說,客戶端發起連線後,如果沒訊息,就一直不返回 Response 給客戶端,連線階段一直是阻塞的。

而 WebSocket 解決了 HTTP 的這幾個難題。當伺服器完成協議升級後( HTTP -> WebSocket ),服務端可以主動推送資訊給客戶端,解決了輪詢造成的同步延遲問題。由於 WebSocket 只需要一次 HTTP 握手,服務端就能一直與客戶端保持通訊,直到關閉連線,這樣就解決了伺服器需要反覆解析 HTTP 協議,減少了資源的開銷。

隨著新標準的推進,WebSocket 已經比較成熟了,並且各個主流瀏覽器對 WebSocket 的支援情況比較好(不相容低版本 IE,IE 10 以下),有空可以看看。

使用 WebSocket 的時候,前端使用是比較規範的,js 支援 ws 協議,感覺類似於一個輕度封裝的 Socket 協議,只是以前需要自己維護 Socket 的連線,現在能夠以比較標準的方法來進行。

下面我們就結合上圖具體來聊一下 WebSocket 的通訊過程。

建立連線

客戶端請求報文 Header

客戶端請求報文:

  1. GET / HTTP/1.1

  2. Upgrade: websocket

  3. Connection: Upgrade

  4. Host: example.com

  5. Origin: http://example.com

  6. Sec-WebSocket-Key: sN9cRrP/n9NdMgdcy2VJFQ==

  7. Sec-WebSocket-Version: 13

與傳統 HTTP 報文不同的地方:

  1. Upgrade: websocket

  2. Connection: Upgrade

這兩行表示發起的是 WebSocket 協議。

  1. Sec-WebSocket

    -Key: sN9cRrP/n9NdMgdcy2VJFQ==

  2. Sec-WebSocket-Version: 13

Sec-WebSocket-Key 是由瀏覽器隨機生成的,提供基本的防護,防止惡意或者無意的連線。

Sec-WebSocket-Version 表示 WebSocket 的版本,最初 WebSocket 協議太多,不同廠商都有自己的協議版本,不過現在已經定下來了。如果服務端不支援該版本,需要返回一個 Sec-WebSocket-Versionheader,裡面包含服務端支援的版本號。

建立 WebSocket 物件:

  1. var ws = new websocket("ws://127.0.0.1:8001");

ws 表示使用 WebSocket 協議,後面接地址及埠

完整的客戶端程式碼:

  1. <scripttype="text/javascript">

  2. var ws;

  3. var box = document.getElementById('box');

  4. function startWS() {

  5.        ws = newWebSocket('ws://127.0.0.1:8001');

  6.        ws.onopen = function (msg) {

  7.            console.log('WebSocket opened!');

  8.        };

  9.        ws.onmessage = function (message) {

  10.            console.log('receive message: ' + message.data);

  11.            box.insertAdjacentHTML('beforeend', '<p>' + message.data + '</p>');

  12.        };

  13.        ws.onerror = function (error) {

  14.            console.log('Error: ' + error.name + error.number);

  15.        };

  16.        ws.onclose = function () {

  17.            console.log('WebSocket closed!');

  18.        };

  19.    }

  20. function sendMessage() {

  21.        console.log('Sending a message...');

  22. var text = document.getElementById('text');

  23.        ws.send(text.value);

  24.    }

  25.    window.onbeforeunload = function () {

  26.        ws.onclose = function () {};  // 首先關閉 WebSocket

  27.        ws.close()

  28.    };

  29. </script>

服務端響應報文 Header

首先我們來看看服務端的響應報文:

  1. HTTP/1.1101SwitchingProtocols

  2. Upgrade: websocket

  3. Connection: Upgrade

  4. Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=

  5. Sec-WebSocket-Protocol: chat

我們一行行來解釋:

  1. 首先,101 狀態碼錶示伺服器已經理解了客戶端的請求,並將通過 Upgrade 訊息頭通知客戶端採用不同的協議來完成這個請求;

  2. 然後, Sec-WebSocket-Accept 這個則是經過伺服器確認,並且加密過後的 Sec-WebSocket-Key;

  3. 最後, Sec-WebSocket-Protocol 則是表示最終使用的協議。

Sec-WebSocket-Accept 的計算方法:

  1. 將 Sec-WebSocket-Key 跟 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 拼接;

  2. 通過 SHA1 計算出摘要,並轉成 base64 字串。

注意: Sec-WebSocket-Key/ Sec-WebSocket-Accept 的換算,只能帶來基本的保障,但連線是否安全、資料是否安全、客戶端 / 服務端是否合法的 ws 客戶端、ws 服務端,其實並沒有實際性的保證。

建立主執行緒,用於實現接受 WebSocket 建立請求:

  1. def create_socket():

  2. # 啟動 Socket 並監聽連線

  3.    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

  4. try:

  5.        sock.bind(('127.0.0.1', 8001))

  6. # 作業系統會在伺服器 Socket 被關閉或伺服器程序終止後馬上釋放該伺服器的埠,否則作業系統會保留幾分鐘該埠。

  7.        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

  8.        sock.listen(5)

  9. exceptExceptionas e:

  10.        logging.error(e)

  11. return

  12. else:

  13.        logging.info('Server running...')

  14. # 等待訪問

  15. whileTrue:

  16.        conn, addr = sock.accept()  # 此時會進入 waiting 狀態

  17.        data = str(conn.recv(1024))

  18.        logging.debug(data)

  19.        header_dict = {}

  20.        header, _ = data.split(r'\r\n\r\n', 1)

  21. for line in header.split(r'\r\n')[1:]:

  22.            key, val = line.split(': ', 1)

  23.            header_dict[key] = val

  24. if'Sec-WebSocket-Key'notin header_dict:

  25.            logging.error('This socket is not websocket, client close.')

  26.            conn.close()

  27. return

  28.        magic_key = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'

  29.        sec_key = header_dict['Sec-WebSocket-Key'] + magic_key

  30.        key = base64.b64encode(hashlib.sha1(bytes(sec_key, encoding='utf-8')).digest())

  31.        key_str = str(key)[2:30]

  32.        logging.debug(key_str)

  33.        response = 'HTTP/1.1 101 Switching Protocols\r\n' \

  34. 'Connection: Upgrade\r\n' \

  35. 'Upgrade: websocket\r\n' \

  36. 'Sec-WebSocket-Accept: {0}\r\n' \

  37. 'WebSocket-Protocol: chat\r\n\r\n'.format(key_str)

  38.        conn.send(bytes(response, encoding='utf-8'))

  39.        logging.debug('Send the handshake data')

  40. WebSocketThread(conn).start()

進行通訊

服務端解析 WebSocket 報文

Server 端接收到 Client 發來的報文需要進行解析

Client 包格式

1.FIN: 佔 1bit

  • 0:不是訊息的最後一個分片

  • 1:是訊息的最後一個分片

2.RSV1, RSV2, RSV3:各佔 1bit

一般情況下全為 0。當客戶端、服務端協商採用 WebSocket 擴充套件時,這三個標誌位可以非 0,且值的含義由擴充套件進行定義。如果出現非零的值,且並沒有採用 WebSocket 擴充套件,連接出錯。

3.Opcode: 4bit

  • %x0:表示一個延續幀。當 Opcode 為 0 時,表示本次資料傳輸採用了資料分片,當前收到的資料幀為其中一個數據分片;

  • %x1:表示這是一個文字幀(text frame);

  • %x2:表示這是一個二進位制幀(binary frame);

  • %x3-7:保留的操作程式碼,用於後續定義的非控制幀;

  • %x8:表示連線斷開;

  • %x9:表示這是一個心跳請求(ping);

  • %xA:表示這是一個心跳響應(pong);

  • %xB-F:保留的操作程式碼,用於後續定義的控制幀。

4.Mask: 1bit

表示是否要對資料載荷進行掩碼異或操作。

    相關推薦

    WebSocket 通訊過程實現

    (點選上方公眾號,可快速關注)來源:wzhvictorsegmentfault.c

    小程式java實現websocket通訊

    1.websocket類 /** * @ServerEndpoint 註解是一個類層次的註解,它的功能主要是將目前的類定義成一個websocket伺服器端, * 註解的值將被用於監聽使用者連線的終端訪問URL地址,客戶端可以通過這個URL來連線到WebSocket伺服器端 */ @Serv

    Android BLE終端通信(三)——client服務端通信過程以及實現數據通信

    .sh 沒有 indexof 實例 解析 rip listview filter @override Android BLE與終端通信(三)——client與服務端通信過程以及實現數據通信 前面的終究僅僅是小知識點。上不了臺面,也僅僅能算是起

    一次完整的http的請求過程https的實現

    http一次完整的http請求過程: (1)發起請求建立連接; 三次握手 接收請求或拒絕請求 (2)接受請求 來自網絡的請求報文中對某資源的一次請求過程; 並發訪問響應模型(Web I/O); 單進程I/O結構:啟動一個進程處理用戶請求,而且一次只處理一個;多個請求被串行響應

    LVM原理實現過程

    LVM原理與實現過程一、什麽是LVM 不管是使用傳統的MBR分區方式或者是GPT的分區方式,在最後數據量逐漸變大的過程中都會出現空間不足的情況,但是若是使用將此分區的數據全部遷移至一個更大空間的磁盤上的遷移時間也是不可想象的,為了解決這個問題,LVM就誕生了。LVM(Logical volume Manag

    ArduinoROS通訊過程中"Lost sync with device, restarting..."錯誤

    對於這個問題的一種可能的解決方案,在回撥函式中使用了while(1)迴圈,當註釋掉迴圈後,這個問題得到了解決。 程式如下:測試的目的是:當得到話題的資料時,則Arduino控制蜂鳴器一直以該頻率發出鈴聲;但後來發現當存在while(1)迴圈時,一直會報錯,註釋掉之後,解決這個問題;大概時回

    Netty4.x下使用HTTPWebSocket通訊協議

        Netty4.x下使用HTTP與WebSocket通訊協議 由於公司需求,需要將Netty框架使用的通訊協議由HTTP轉成WebSocket協議。由於初學Netty忙了一天,做了一下總結。首先介紹下兩者: HTTP協議

    Python使用CtypesC/C++ DLL檔案通訊過程介紹及例項分析

    專案中可能會經常用到第三方庫,主要是出於程式效率考慮和節約開發時間避免重複造輪子。無論第三方庫開源與否,程式語言是否與當前專案一致,我們最終的目的是在當前程式設計環境中呼叫庫中的方法並得到結果或者藉助庫中的模組實現某種功能。這個過程會牽涉到很多東西,本篇文章將簡要的介紹一下該過程的一些問題。 1.背景 多

    Dubbo原始碼分析:RPC協議實現-RPC過程核心介面設計

    RPC的基本過程 提供者Provider:提供服務的介面定義和介面的具體實現,然後通過URL的方式告訴消費者,某個URL對應某個service實現,一般是將服務的資訊註冊到一個註冊中心,如zookeeper或者Redis等; 消費者Consumer:獲取提供者的介面定義

    MPPT演算法(恆定電壓、擾動觀察、電導增量)介紹實現過程

    1、太陽能板的特性曲線 太陽能板也叫光伏電池。是通過光電效應,把光能轉換為電能的裝置。 先介紹太陽能板的特性。太陽能的額定引數是在地面光伏元件標準測試條件(STC)條件下測量得到的。 STC有三個條件:第一、光線通過大氣的實際距離為大氣垂直厚度的1.5倍。第二、

    瀏覽器go語言的websocket通訊

    簡介WebSocket是HTML5一種新的協議。顧名思義,它在伺服器和瀏覽器之間建立了全雙工通訊。需求背景區塊鏈測試系統web前端平臺需要動態接收後端傳送的狀態資訊改變一次測試session過程的狀態顯示,測試用例在執行後會返回一次執行的session,後端根據該session實時返回測試狀態。思路過程:1.

    C++ 百萬併發網路通訊引擎架構實現視訊課程(雲盤下載)

    課程目錄8 K7 u" j$ c+ j- k/ c9 g3 T0 b        第1章第1章 搭建多平臺下C++開發環境7小時16分鐘22節                1-1課程介紹 32:124 s1 E7 {* l. P" T2 U               

    最完整C++ 百萬併發網路通訊引擎架構實現視訊課程

    課程目錄8 K7 u" j$ c+ j- k/ c9 g3 T0 b        第1章第1章 搭建多平臺下C++開發環境7小時16分鐘22節                1-1課程介紹 32:124 s1 E7 {* l. P" T2 U               

    vue使用SockJS實現webSocket通訊

    以前使用websocket都是使用 window.webSocket = new WebSocket('ws://' + config.webSocketUrl + '/webData/websocket?token=' + token + '&username=' + username);這種方

    .NET Core微服務之路:利用DotNetty實現一個簡單的通訊過程

      上一篇我們已經全面的介紹過《基於gRPC服務發現與服務治理的方案》,我們先複習一下RPC的呼叫過程(筆者會在這一節的幾篇文章中反覆的強調這個過程呼叫方案),看下圖

    ActivityFragment通訊 多介面實現優化

    1.問題 我們都知道Activity與Fragment通訊的幾種方式,推崇的應該是谷歌給出的介面方案。 地址:https://developer.android.com/training/basics/fragments/communicating.html#DefineInterface

    VUE+SpringBoot+Apache2.4實現websocket通訊

    需求:實現訊息主動推送 後端實現(Spring Boot): 1、引入websocket包 <dependency> <groupId>org.springframework.boot</groupId> <

    Netty實現WebSocket通訊案例

    Netty開發服務端,HTML實現客戶端,實現服務端與客戶端的實時互動。 1.儲存整個工程的全域性配置: package com.research.netty.WebSocket; import io.netty.channel.group.ChannelGrou

    [連載]《C#通訊(串列埠和網路)框架的設計實現》- 0.前言

                                  目       錄 前言 前言       剛參加工作,使用過VB、VC開發軟體,隨著C#的崛起,聽說是C++++,公司決定以後開發軟體使用C#,憑藉在書市5塊錢買C#程式設計入門書籍,開始了職業生涯。開發C/S、B/S結構的軟體是

    [連載]《C#通訊(串列埠和網路)框架的設計實現》-2.框架的總體設計

    目       錄 C#通訊(串列埠和網路)框架的設計與實現... 1 (SuperIO)- 框架的總體設計... 1 第二章           框架總體的設計... 2 2.1           宿主程式設計... 2 2.2           通訊機制設計... 7   2.2.1