實現服務端和客戶端的實時雙向數據傳輸-WebSocket簡單了解

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