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:
客戶端執行:
當伺服器關閉服務: