實現服務端和客戶端的實時雙向數據傳輸-WebSocket簡單了解
- 2021 年 4 月 26 日
- 筆記
- javascript, 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:
客戶端運行:
當服務器關閉服務: