首頁>技術>

大部分基於JavaScript的WebSocket協議示例都是基於nodejs作為服務端,瀏覽器作為客戶端,很少有nodejs的客戶端實現。同時,由於大部分nodejs客戶端的WebSocket協議和瀏覽器原生的WebSocket並不完全相容,因此他們無法很好地在一起工作。

本倉庫包含了一系列WebSocket協議在nodejs下的不同實現,包括純的TypeScript語言實現以及Express或Nestjs框架的實現。

每個例子在單獨的示例資料夾下,其子資料夾用以區分不同的客戶端和伺服器。

1. Websocket 基礎

WebSocket是用於"在網路應用中保持和伺服器端程序雙邊通訊的",這一功能和傳統的http協議僅能由客戶端發起請求伺服器進行響應有所區別。 在實現上,websocket協議分為 官方實現 和非官方實現

npmjs上3個最流行的websocket庫分別是 ws、socket.io、and websocket(其他常見的websocket庫還有WebSocket-Node、µWebSockets等). 我們在這裡使用了前兩個,因為他們各自有其優點和不足。

ws庫是最流行的websoket庫,它完全支援官方協議。在瀏覽器中可以不用引入額外的包,直接用瀏覽器原生Websocket實現功能,然而,如果你要在nodejs上使用ws作為客戶端,你還需要一個類似像我們這裡用的 isomorphic-ws 的包裝器。

socket.io雖然和官方庫實現不相容,但有有其自身的特點,在負載均衡、穿過防火牆以及自動重連方面都有其優勢。使用該庫時需要在伺服器和客戶端同時引入,注意,在客戶端要引入socket.io-client y庫而不是 socket.io.

2. 示例的基礎功能

示例用以說明websocket的不同實現,因此不會實現更復雜的功能共,每個示例在庫檔案功能允許的情況下包含以下功能,示例中部分程式碼來自各庫的官方示例:

一個純nodejs的伺服器或者基於網路框架的伺服器一個瀏覽器實現的客戶端和一個nodejs(包含或者不包含框架)實現的客戶端執行伺服器和客戶端(網頁瀏覽器只需要在網頁中開啟client.html檔案),客戶端將自動連線伺服器客戶端連線後,伺服器向客戶端傳送一個 hello 訊息,包含 hello client 資料客戶端收到 message訊息後傳送一個 response 訊息,包含 response + 隨機數 到伺服器伺服器收到 response 訊息後,傳送 end訊息幷包含其收到的訊息內容。客戶端收到end訊息後z延時3秒斷開連線伺服器和客戶端均需要在終端列印其收到的所有訊息伺服器和客戶端均需要列印連線狀態變化資訊伺服器和客戶端均需要列印錯誤資訊3. 示例檔案說明3.1 nodejs ws example

本資料夾包括使用純nodejs實現的WebSocket伺服器和客戶端示例。

其中server資料夾包括了伺服器檔案,安裝並透過npm run start即可編譯啟動。server資料夾下還包括了一個執行在http伺服器上的ws 伺服器 server2.ts,透過 npm run start2 啟動,為簡化示例,兩個伺服器均使用 18000埠(http伺服器監聽3000埠).

client資料夾包括了:

一個純瀏覽器的客戶端實現client.html,使用瀏覽器開啟即可一個使用ws實現的客戶端client.ts,執行npm run start執行一個相容瀏覽器標準協議的客戶端client2.ts,執行npm run start2執行

說明:

原生ws和相容瀏覽器標準的WebSocket協議之間的主要區別.在原生ws中函式格式為

ws.on('message', (message)=>{    console.log('received: %s', message);});

在相容瀏覽器標準的WebSocket協議中書寫格式為:

ws.onmessage=(message)=>{    console.log('received: %s', message);}

除此之外,兩個協議中message的型別和內容也不同,原生協議的message型別為WebSocket.data,而瀏覽器標準的協議中message型別為WebSocket.MessageEvent,後者的結構類似如下,而前者僅包含其中data的內容:

 MessageEvent {  target: [WebSocket],  type: 'message',  data: '{"event":"end","data":"response: 0.3416359669492526"}' }
3.2 nodejs socketio example

本資料夾包括使用純nodejs實現的socket.io伺服器和客戶端示例。

其中server和client資料夾分別包含了伺服器和客戶端檔案,安裝並透過npm run start即可編譯啟動。server資料夾下還包括了一個執行在http伺服器上的socket.io 伺服器 server2.ts,透過 npm run start2 啟動,為簡化示例,兩個伺服器均使用 18000埠.

client資料夾同時包含了index.html瀏覽器客戶端,需要注意:

由於socket.io與標準WebSocket協議不相容,因此瀏覽器需要依賴socket.io.js檔案執行,也可能需要socket.io.js.map。這兩個檔案在socket.io官方庫的client-dist中可以找到。由於版本升級,使用v2版本的socket.io檔案連線v3版本伺服器可能會出現400錯誤。此外,由於socket.io v3 開啟了CORS,透過瀏覽器直接開啟index.html無法正常執行客戶端,本案例安裝了http-server庫作為網頁伺服器(預設開啟index.html檔案),並在伺服器中配置CORS(見下文),透過npm run startweb執行http-server並訪問http://localhost:8081以透過瀏覽器訪問。

說明:

本倉庫socket.io v3,socket.io從版本2到3的升級過程中有很大變動,因此本倉庫示例並不適用於socket.io v2版本。參考官方文件跟本倉庫示例有關的幾點說明如下。

由於socket.io v3程式碼庫使用TypeScript重寫,因此在使用TypeSctipt時不需要再引入@types/socket.io或@types/socket.io-client庫,如果繼續引入可能會在編譯時出錯。在版本3中,跨域請求CORS需要在新建伺服器時明確宣告,例如:
const io = require("socket.io")(httpServer, {  cors: {    origin: "https://localhost:8081",    credentials: true,    methods: ["GET", "POST"]  }});
socket.io通訊可配置自動重連機制,該機制預設開啟,在伺服器連線斷開後會自動嘗試重連伺服器,如果要測試該功能,可註釋掉客戶端setTimeout函式內容,關閉伺服器後再開啟,客戶端即可重新連線。socket.io支援訊息確認機制,本示例的response訊息增加了訊息確認機制,伺服器在收到訊息後會透過回撥函式進行確認。socket.io訊息的內容型別可相容string或JSON等格式,使用者需要在程式設計時自行決定資料型別並盡心解析。3.3 nestjs ws example

本資料夾基於ws庫使用流行的nestjs框架分別實現了伺服器和客戶端的WebSocket服務,由於Nestjs是一個伺服器框架,因此,在客戶端部分,又透過nestjs內建的靜態檔案和mvc框架服務設計了一個html網頁以便進行功能測試。本資料夾案例和 1. nodejs ws example資料夾中的案例介面設計基本相同,可以互相連線,但本案例為了豐富功能測試,在功能上進行了以下修改和補充:

伺服器在收到客戶端的response訊息後,並不會立即返回end訊息,而是同樣返回一個response訊息,以避免客戶端過早斷開連線影響測試。客戶端新增了一個response訊息響應,將收到的訊息進行列印顯示客戶端新增了一個terminate訊息,需要手動觸發以傳送,伺服器收到terminate訊息後,傳送end訊息,客戶端再斷開連線

為了測試客戶端的http相應功能,在客戶端新增了兩個api分別為,為了避免客戶端和伺服器http埠衝突,修改客戶端埠為3001:

-Get http://localhost:3001/ws-client/getdata 用於傳送並接受response訊息,透過隨機數來更新資料

-Get http://localhost:3001/ws-client/sendTerminate 用於傳送terminate訊息以斷開連線,該命令會停止客戶端程式執行

在客戶端根目錄下新建了client.http檔案,該檔案可以透過vscode的REST Client外掛執行,用於進行api功能除錯。

經過測試,ws庫可以不需要http伺服器啟用cors功能。

使用步驟:

分別在server和client資料夾下執行npm run start即可啟動伺服器和客戶端,可以看到websocket通訊。在瀏覽器中開啟http://localhost:3001可以在console中和網頁中看到資料,透過重新整理按鈕可以重新整理資料。本示例和 1. nodejs ws example中的示例可以互通互聯,但由於api不同,使用時應注意區別。3.4 nestjs socket io example

本資料夾基於socket.io庫使用流行的nestjs框架分別實現了伺服器和客戶端的WebSocket服務, API介面和示例3完全相同,但有一些 重點注意事項:

由於socket.io的3.x和2.x版本不相容,他們無法協同工作,也就是說伺服器和客戶端要工作在同一個大版本下。本案例使用版本3.Nestjs 內建的socket.io庫@nestjs/platform-io是基於socket.io版本2,與版本3的客戶端不相容。Nestjs 作者答應在nestjs 8中升級 socket-io版本,目前,如果要像本示例一樣使用socket.io v3,可以參考nestjs issue 5676問題中給出的臨時策略, 用socket-io.adapter而不是官方的介面卡。(要刪除cookie項下面的name一行) .當然,將客戶端降級到2.x版本可能也能工作,但本案例並未嘗試此種做法。到目前為止,客戶端和伺服器可以正常連線工作,但是如果使用案例2中的客戶端,是連線不上本案例的伺服器的,這可能是由@WebSocketGateway()裝飾器造成的,必須把裝飾器的port引數留空,或者配置為和伺服器http埠一致,才可以正常訪問,如果兩者不一致,即使配置了cors仍然是無法透過案例2中的客戶端訪問本案例中的伺服器的。4. 原始碼倉庫

本文原始碼的github倉庫在: https://github.com/weizy0219/nodejs_websocket_examples

參考資料HTML線上標準官方WebSocket協議ws庫socket.io 庫websocket 庫isomorphic-ws 庫socket.io-client 庫socket.io 網站版本遷移說明Nestjs issue 5676

14
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 阿里的運維大牛教你:如何程式設計模擬計算機中的快取記憶體