實現服務端和客戶端的實時雙向資料傳輸-WebSocket簡單瞭解

耶溫發表於2021-04-26

WebSocket

前段時間專案中遇到了訊息推送的問題,當時採用客戶端輪詢,每隔 5s 請求一次資料。由於輪詢的效率低,非常浪費資源。後面準備把輪詢調整為使用 WebSocket 來建立連線,實現推送。

WebSocket 介紹

一種網路通訊協議,使用 WebSocket 伺服器可以主動向客戶端推送資訊,客戶端也可以主動向伺服器傳送資訊,實現真正的雙向平等對話,可以實現伺服器推送功能。並且現在瀏覽器中都已實現。

與 HTTP 比對

HTTP 協議,通訊只能客戶端發起。
比如:在接收提示訊息時,只能客戶端向伺服器端傳送請求才知道有沒有最新的訊息。不能伺服器端直接通知客戶端有最新的訊息。簡單來說就是 HTTP 做不到資訊的推送。只能通過客戶端每隔一段時間傳送請求查詢。

而 HTTP2.0 則是對 HTML、CSS 等 JS 資源的傳輸方式進行了優化,並沒有提供新的 JS API,也不能用於實時傳輸訊息。

WebSocket 特點

  • 客戶端與伺服器端實現容易(建立在 TCP 協議之上)

  • 沒有同源限制可以與任意伺服器建立通訊

  • 協議表示符為 ws 或者 wws(加密協議),伺服器地址就是 url 地址

    • 比如:ws://demo.example.com:8001

客戶端使用

在客戶端中使用很簡單,只需要開啟一個 WebSocket 服務並且實現監聽伺服器傳送的訊息即可。

// 開啟WebSocket服務
var ws = new WebSocket("ws://127.0.0.1:8001");
ws.onopen = function (data) {
  // 傳送資訊
  ws.send("hello World");
};
ws.onmessage = function (data) {
  console.log(data.data); // 接收資訊
};
ws.onclose = function (data) {
  console.log("關閉", data); //關閉
};

如上程式碼,我們可以使用 ws.send()傳送資料,也可以適用 ws.onmessage 監聽伺服器端傳送的資訊資料,實現簡單的實時雙向傳輸功能。

WebSocket 事件

我們在開始 WebSocket 服務之後,可以直接使用 addEventListener() 或將一個事件監聽器賦值給介面的 oneventname 屬性,來監聽相關事件。

clone

當一個 WebSocket 連線被關閉時觸發。
也可以通過 onclose 屬性來設定。

ws.addEventListener("clone", function (event) {
  // WebSocket 已關閉
});

//或者
ws.onclone = function (event) {
  // WebSocket 已關閉
};

如上面程式碼所示,WebSocket 的相關事件都可以通過這兩種方法來監聽實現。

error

當一個 WebSocket 連線因錯誤而關閉時觸發,例如無法傳送資料時。
也可以通過 onerror 屬性來設定.

ws.addEventListener("error", function (event) {
  // WebSocket 出現錯誤
});

//或者
ws.onerror = function (event) {
  // WebSocket 出現錯誤
};

message

當通過 WebSocket 收到資料時觸發。
也可以通過 onmessage 屬性來設定。

ws.addEventListener("message", function (event) {
  // WebSocket 監聽訊息 收到伺服器端訊息是觸發
});

//或者
ws.onmessage = function (event) {
  // WebSocket 監聽訊息 收到伺服器端訊息是觸發
};

open

當一個 WebSocket 連線成功時觸發。
也可以通過 onopen 屬性來設定。

ws.addEventListener("open", function (event) {
  // WebSocket 連線成功。
});

//或者
ws.onopen = function (event) {
  // WebSocket 連線成功。
};

客戶端屬性與方法

WebSocket.readyState

該屬性返回當前的例項物件狀態(只讀):

  • CONNECTING:值為 0,正在連結中
  • OPEN:值為 1,已經連結並且可以通訊
  • CLOSING:值為 2,連線正在關閉
  • CLOSED:值為 3,連線已關閉或者沒有連結成功

WebSocket.bufferedAmount

未傳送至伺服器的位元組數(只讀)。當 WebSocket.bufferedAmount 為 0 時說明傳送成功。在實際專案中可以該屬性來判斷是否傳送完成。

if (ws.bufferedAmount === 0) {
  // 傳送成功
} else {
  // 傳送未結束
}

其他屬性

  • WebSocket.url WebSocket 的絕對路徑(只讀)。
  • WebSocket.protocol 伺服器選擇的下屬協議(只讀)。
  • WebSocket.extensions 返回伺服器已選擇的擴充套件值。目前,連結可以協定的擴充套件值只有空字串或者一個擴充套件列表(只讀)。
  • WebSocket.binaryType 返回 websocket 連線所傳輸二進位制資料的型別。

WebSocket.send()

對要傳輸的資料進行排隊。

WebSocket.send() 方法將需要通過 WebSocket 連結傳輸至伺服器的資料排入佇列,並根據所需要傳輸的 data bytes 的大小來增加 bufferedAmount 的值 。若資料無法傳輸(例如資料需要快取而緩衝區已滿)時,套接字會自行關閉。

傳送資料型別需是下面幾個型別之一:

  • USVString:文字字串,字串將以 UTF-8 格式新增到緩衝區,並且 bufferedAmount 將加上該字串以 UTF-8 格式編碼時的位元組數的值。
  • ArrayBuffer:可以使用一有型別的陣列物件傳送底層二進位制資料;其二進位制資料記憶體將被快取於緩衝區,bufferedAmount 將加上所需位元組數的值。
  • Blob:Blob 型別將佇列 blob 中的原始資料以二進位制中傳輸。 bufferedAmount 將加上原始資料的位元組數的值。
  • ArrayBufferView:可以以二進位制幀的形式傳送任何 JavaScript 類陣列物件 ;其二進位制資料內容將被佇列於緩衝區中。值 bufferedAmount 將加上必要位元組數的值。
// 文字:

ws.send("hello world");

// ArrayBuffer
const buffer = new ArrayBuffer(8);
ws.send(buffer);

// Blob
var demo = { hello: "world" };
var blob = new Blob([JSON.stringify(demo, null, 2)], {
  type: "application/json",
});
ws.send(blob);

WebSocket.close()

關閉當前連結。

兩個可選引數:

  • code: 可選,一個數字狀態碼,它解釋了連線關閉的原因。如果沒有傳這個引數,預設使用 1005
  • reason: 可選,可讀的字串,它解釋了連線關閉的原因。

事件型別

  • WebSocket.onclose 用於指定連線關閉後的回撥函式。
  • WebSocket.onerror 用於指定連線失敗後的回撥函式。
  • WebSocket.onmessage 用於指定當從伺服器接受到資訊時的回撥函式。
  • WebSocket.onopen 用於指定連線成功後的回撥函式。

服務端使用

如果各個伺服器端語言實現方法不一樣,這裡簡單說下我的測試環境。

測試使用的是 node.js 配合 nodejs-websocket 進行的

具體使用如下:

// 引入nodejs-websocket
var ws = require("nodejs-websocket");
// 建立websocket
var server = ws
  .createServer(function (conn) {
    // 監聽訊息
    conn.on("text", function (str) {
      // 呼叫相關方法
      loadMessage(conn, str);
    });
    // 監聽關閉
    conn.on("close", function (code, reason) {
      console.log("關閉");
    });
  })
  .listen(8001);
console.log("開啟成功~");

// 接收訊息 並返回資訊
function loadMessage(conn, str) {
  conn.send(str + "獲取到訊息,已接收訊息,已返回回答");
}

最後附上執行截圖

開啟伺服器端WebSocket:

image

客戶端執行:

image

當伺服器關閉服務:

image

相關文章