應用場景:
用戶端與浏覽器間進行通信,正常操作,我們在用戶端發送一個http請求,服務端收到我們的http請求後,傳回一個請求結果。這種方式,我們隻能主動的從服務端去擷取資料,假若想服務端資料有更新第一時間回報到用戶端,我們再利用http請求通信就行不通了。接下來我們來探讨一下WebSocket如何去實作上述情景中所提應用需求。
WebSocket簡介
首先了解一下,Socket又稱"套接字",應用程式通常通過"套接字"向網絡送出請求或者應答網絡請求。Socket的英文原義是“孔”或“插座”,作為UNIX的程序通信機制。Socket可以實作應用程式間網絡通信。
WebSocket是HTML5新增加的(協定),也就是說HTTP協定沒有變化,或者說和HTTP沒關系,但HTTP是不支援持久連接配接的。 Websocket 其實是一個新協定,跟HTTP協定基本沒有關系,隻是為了相容現有浏覽器的握手規範而已,也就是說它是HTTP協定上的一種補充。它們有交集,但不等于。
簡而言之:WebSocket 是 HTML5 開始提供的一種在單個 TCP 連接配接上進行全雙工通訊的協定。
為什麼需要 WebSocket ?
了解計算機網絡協定的人,應該都知道:HTTP 協定是一種無狀态的、無連接配接的、單向的應用層協定。它采用了請求/響應模型。通信請求隻能由用戶端發起,服務端對請求做出應答處理。
這種通信模型有一個弊端:HTTP 協定無法實作伺服器主動向用戶端發起消息。
這種單向請求的特點,注定了如果伺服器有連續的狀态變化,用戶端要獲知就非常麻煩。大多數 Web 應用程式将通過頻繁的異步JavaScript和XML(AJAX)請求實作長輪詢。輪詢的效率低,非常浪費資源(因為必須不停連接配接,或者 HTTP 連接配接始終打開)。
WebSocket 連接配接允許用戶端和伺服器之間進行全雙工通信,以便任一方都可以通過建立的連接配接将資料推送到另一端。WebSocket 隻需要建立一次連接配接,就可以一直保持連接配接狀态。這相比于輪詢方式的不停建立連接配接顯然效率要大大提高。
了解完WebSocket是什麼,以及為什麼需要WebSocket,我們更需要知道怎麼去使用它,作為前端開發的角色在前端上我們該如何使用呢。
WebSocket使用
使用方法:
var ws = new WebSocket("wss://echo.websocket.org");
ws.onopen = function(evt) {
console.log("Connection open ...");
ws.send("Hello WebSockets!");
};
ws.onmessage = function(evt) {
console.log( "Received Message: " + evt.data);
ws.close();
};
ws.onclose = function(evt) {
console.log("Connection closed.");
};
WebSocket API
可參考 https://developer.mozilla.org/en-US/docs/Web/API/WebSocket
1) WebSocket 構造函數
WebSocket 對象作為一個構造函數,用于建立 WebSocket 執行個體。
var ws = new WebSocket('ws://localhost:8080');
執行上面語句之後,用戶端就會與伺服器進行連接配接。
2)webSocket.readyState
readyState屬性傳回執行個體對象的目前狀态,共有四種。
CONNECTING:值為0,表示正在連接配接。
OPEN:值為1,表示連接配接成功,可以通信了。
CLOSING:值為2,表示連接配接正在關閉。
CLOSED:值為3,表示連接配接已經關閉,或者打開連接配接失敗。
下面是一個示例。
switch (ws.readyState) {
case WebSocket.CONNECTING:
// do something
break;
case WebSocket.OPEN:
// do something
break;
case WebSocket.CLOSING:
// do something
break;
case WebSocket.CLOSED:
// do something
break;
default:
// this never happens
break;
}
3)webSocket.onopen
執行個體對象的onopen屬性,用于指定連接配接成功後的回調函數。
ws.onopen = function () {
ws.send('Hello Server!');
}
如果要指定多個回調函數,可以使用addEventListener方法。
ws.addEventListener('open', function (event) {
ws.send('Hello Server!');
});
4)webSocket.onclose
執行個體對象的onclose屬性,用于指定連接配接關閉後的回調函數。
ws.onclose = function(event) {
var code = event.code;
var reason = event.reason;
var wasClean = event.wasClean;
// handle close event
};
ws.addEventListener("close", function(event) {
var code = event.code;
var reason = event.reason;
var wasClean = event.wasClean;
// handle close event
});
5)webSocket.onmessage
執行個體對象的onmessage屬性,用于指定收到伺服器資料後的回調函數。
ws.onmessage = function(event) {
var data = event.data;
// 處理資料
};
ws.addEventListener("message", function(event) {
var data = event.data;
// 處理資料
});
注意,伺服器資料可能是文本,也可能是二進制資料(blob對象或Arraybuffer對象)。
ws.onmessage = function(event){
if(typeof event.data === String) {
console.log("Received data string");
}
if(event.data instanceof ArrayBuffer){
var buffer = event.data;
console.log("Received arraybuffer");
}
}
除了動态判斷收到的資料類型,也可以使用binaryType屬性,顯式指定收到的二進制資料類型。
// 收到的是 blob 資料
ws.binaryType = "blob";
ws.onmessage = function(e) {
console.log(e.data.size);
};
// 收到的是 ArrayBuffer 資料
ws.binaryType = "arraybuffer";
ws.onmessage = function(e) {
console.log(e.data.byteLength);
};
6)webSocket.send()
執行個體對象的send()方法用于向伺服器發送資料。
發送文本的例子。
ws.send('your message');
發送 Blob 對象的例子。
var file = document
.querySelector('input[type="file"]')
.files[0];
ws.send(file);
發送 ArrayBuffer 對象的例子。
// Sending canvas ImageData as ArrayBuffer
var img = canvas_context.getImageData(0, 0, 400, 320);
var binary = new Uint8Array(img.data.length);
for (var i = 0; i < img.data.length; i++) {
binary[i] = img.data[i];
}
ws.send(binary.buffer);
7)webSocket.bufferedAmount
執行個體對象的bufferedAmount屬性,表示還有多少位元組的二進制資料沒有發送出去。它可以用來判斷發送是否結束。
var data = new ArrayBuffer(10000000);
socket.send(data);
if (socket.bufferedAmount === 0) {
// 發送完畢
} else {
// 發送還沒結束
}
8)webSocket.onerror
執行個體對象的onerror屬性,用于指定報錯時的回調函數。
socket.onerror = function(event) {
// handle error event
};
socket.addEventListener("error", function(event) {
// handle error event
});
前後端配合,即可完成資訊的實時推送。