Web Socket
? Web Socket(套接字)的目標是通過一個長時連接實現與服務器全雙工、雙向的通信。在 JavaScript 中創建 Web Socket 時,一個 HTTP 請求會發送到服務器以初始化連接。服務器響應后,連接使用 HTTP 的 Upgrade 頭部從 HTTP 協議切換到 Web Socket 協議。這意味著 Web Socket 不能通過標準 HTTP 服務器實現,而必須使用支持該協議的專有服務器。
? 因為 Web Socket 使用了自定義協議,所以 URL方案(scheme)稍有變化:不能再使用 http://或 https://, 而要使用 ws://和 wss://。前者是不安全的連接,后者是安全連接。在指定 Web Socket URL 時,必須包含 URL 方案,因為將來有可能再支持其他方案。
? 使用自定義協議而非 HTTP 協議的好處是,客戶端與服務器之間可以發送非常少的數據,不會對 HTTP 造成任何負擔。使用更小的數據包讓 Web Socket 非常適合帶寬和延遲問題比較明顯的移動應用。 使用自定義協議的缺點是,定義協議的時間比定義 JavaScript API 要長。Web Socket 得到了所有主流瀏覽器支持。
? Web Socket 是與服務器的全雙工、雙向通信渠道。與其他方案不同,Web Socket 不使用 HTTP,而使用了自定義協議,目的是更快地發送小數據塊。這需要專用的服務器,但速度優勢明顯。
API
? 要創建一個新的 Web Socket,就要實例化一個 WebSocket 對象并傳入提供連接的 URL:
let socket = new WebSocket("ws://www.example.com/server.php");
? 注意,必須給 WebSocket 構造函數傳入一個絕對 URL。同源策略不適用于 Web Socket,因此可以打開到任意站點的連接。至于是否與來自特定源的頁面通信,則完全取決于服務器。(在握手階段就可以確定請求來自哪里。)
? 瀏覽器會在初始化 WebSocket 對象之后立即創建連接。與 XHR 類似,WebSocket 也有一個 readyState 屬性表示當前狀態。不過,這個值與 XHR 中相應的值不一樣。
- WebSocket.OPENING(0):連接正在建立。
- WebSocket.OPEN(1):連接已經建立。
- WebSocket.CLOSING(2):連接正在關閉。
- WebSocket.CLOSE(3):連接已經關閉。
? WebSocket 對象沒有 readystatechange 事件,而是有與上述不同狀態對應的其他事件。 readyState 值從 0 開始。
? 任何時候都可以調用 close() 方法關閉 Web Socket 連接:
socket.close();
? 調用 close() 之后,readyState 立即變為 2(連接正在關閉),并會在關閉后變為 3(連接已經關閉)。
發送和接收數據
? 打開 Web Socket 之后,可以通過連接發送和接收數據。要向服務器發送數據,使用 send() 方法并傳入一個字符串、ArrayBuffer 或 Blob,如下所示:
let socket = new WebSocket("ws://www.example.com/server.php"); let stringData = "Hello world!";
let arrayBufferData = Uint8Array.from(['f', 'o', 'o']);
let blobData = new Blob(['f', 'o', 'o']); socket.send(stringData);
socket.send(arrayBufferData.buffer);
socket.send(blobData);
? 服務器向客戶端發送消息時,WebSocket 對象上會觸發 message 事件。這個 message 事件與其他消息協議類似,可以通過 event.data 屬性訪問到有效載荷:
socket.onmessage = function(event) { let data = event.data; // 對數據執行某些操作
};
? 與通過 send() 方法發送的數據類似,event.data 返回的數據也可能是 ArrayBuffer 或 Blob。 這由 WebSocket 對象的 binaryType 屬性決定,該屬性可能是"blob"或"arraybuffer"。
其他事件
? WebSocket 對象在連接生命周期中有可能觸發 3 個其他事件。
- open:在連接成功建立時觸發。
- error:在發生錯誤時觸發。連接無法存續。
- close:在連接關閉時觸發。
? WebSocket 對象不支持 DOM Level 2 事件監聽器,因此需要使用 DOM Level 0 風格的事件處理程序來監聽這些事件:
let socket = new WebSocket("ws://www.example.com/server.php");
socket.onopen = function() { alert("Connection established.");
};
socket.onerror = function() { alert("Connection error.");
};
socket.onclose = function() { alert("Connection closed.");
};
? 在這些事件中,只有 close 事件的 event 對象上有額外信息。這個對象上有 3 個額外屬性: wasClean、code 和 reason。
- wasClean 是一個布爾值,表示連接是否干凈地關閉;
- code 是一個來自服務器的數值狀態碼;
- reason 是一個字符串,包含服務器發來的消息。可以將這些信息顯示給用戶或記錄到日志:
socket.onclose = function(event) { console.log(`as clean? ${event.wasClean} Code=${event.code} Reason=${event.reason}`);
};
?