簡介
服務端和客戶端應該怎么進行通信呢?我們常見的方法就是客戶端向服務器端發送一個請求,然后服務器端向客戶端發送返回的響應。這種做法比較簡單,邏輯也很清晰,但是在某些情況下,這種操作方式並不好使。
比如在服務器端的某些變動需要通知客戶端的情況,因為客戶端並不知道服務器端的變動是否完成,所以需要不停的使用輪循去檢測服務器的狀態。這種做法的缺點就是太過於浪費資源。如果希望及時性好的話,需要不斷的減少輪循的時間間隔,導致極大的服務器壓力和資源的浪費。
那么有沒有好的解決辦法呢?
既然不能使用查詢,那么就改成服務器推送就行了。我們知道在HTTP/2中,提供了一種服務器推送的方式,但是這種方式是單向的,也就是說在同一個TCP連接之上,並不能實現客戶端和服務器端的交互。
於是我們需要一個能夠雙向交互的網絡協議,這個協議就是WebSocket。
webSocket vs HTTP
webSocket是一個基於底層TCP協議的一個雙向通信網絡協議。這個雙向通信是通過一個TCP連接來實現的。webSocket於2011年以RFC 6455發布成為IETF的標准。
同樣作為基於TCP協議的標准協議,它和HTTP有什么區別呢?
如果以OSI的七層模型來說,兩者都位於七層協議的第四層。但是兩者是兩種不同的協議。鑒於HTTP已經如此流行了,為了保證webSocket的通用性,webSocket也對HTTP協議進行了兼容。也就是說能夠使用HTTP協議的地方也就可以使用webScoket。
這個和之前討論的HTTP3有點類似,雖然HTTP3是一個新的協議,但是為了保證其廣泛的應用基礎,HTTP3還是在現有的UDP協議上進行重寫和構建。目的就是為了兼容。
實時上,webSocket使用的是HTTP upgrade header,從HTTP協議升級成為webSocket協議。
HTTP upgrade header
什么是HTTP upgrade header呢?
HTTP upgrade header是在HTTP1.1中引入的一個HTTP頭。當客戶端覺得需要升級HTTP協議的時候,會向服務器端發送一個升級請求,服務器端會做出相應的響應。
對於websocket來說,客戶端在和服務器端建立連接之后,會首先發送給服務器端 Upgrade: WebSocket 和 Connection: Upgrade 頭。服務器端接收到客戶端的請求之后,如果支持webSocket協議,那么會返回同樣的Upgrade: WebSocket和Connection: Upgrade 頭到客戶端。客戶端接收到服務器端的響應之后,就知道服務器端支持websocket協議了,然后就可以使用WebSocket協議發送消息了。
websocket的優點
其實前面我們也講過了,相對於傳統的HTTP拉取,webSocket可以借助於一個TCP連接實現數據的實時傳輸。可以在減少服務器壓力的同時,實現服務器和客戶端的實時通信。
webScoket的應用
WebSocket使用的是ws和wss作為URI的標記符。其中ws表示的是websocket,而wss表示的是WebSocket Secure。
因為通常來說我們使用的web瀏覽器來和服務器進行通信。瀏覽器就是我們的web客戶端,對於現代瀏覽器來說,基本上都支持WebSocket協議,所以大家可以放心應用,不用擔心協議兼容的問題。
對於瀏覽器客戶端來說,可以使用標准的瀏覽器WebSocket對象,來和服務器進行通信,我們看一個簡單的javascript客戶端使用webSocket進行通信的例子:
// 使用標准的WebSocket API創建一個socket連接
const socket = new WebSocket('ws://www.flydean.com:8000/webscoket');
// 監聽webSocket的open事件
socket.onopen = function () {
setInterval(function() {
if (socket.bufferedAmount == 0)
socket.send(getUpdateData());
}, 50);
};
// 監聽接收消息事件
socket.onmessage = function(event) {
handleUpdateData(event.data);
};
// 監聽socket關閉事件
socket.onclose = function(event) {
onSocketClose(event);
};
// 監聽error事件
socket.onerror = function(event) {
onSocketError(event);
};
上述代碼主要就是各種監聽socket的事件,然后進行處理,非常簡單。
websocket的握手流程
上面我們講過了,websocket是從HTTP協議升級的,客戶端通過發送:
Upgrade: websocket
Connection: Upgrade
到服務器端,對協議進行升級。我們舉一個具體的例子:
GET /webscoket HTTP/1.1
Host: www.flydean.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x123455688xafe=
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://flydean.com
對應的server端的返回:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: Qhfsfew12445m=
Sec-WebSocket-Protocol: chat
在上面的例子中,除了使用Upgrade頭之外,客戶端還向服務器端發送了Sec-WebSocket-Key header。這個header包含的是一個 base64 編碼的隨機字節。server對應的會返回這個key的hash值,並將其設置在Sec-WebSocket-Accept header中。
這里並不是為了安全操作,而是為了避免上一次的連接緩存情況。
WebSocket API
要想在瀏覽器端使用WebSocket,那么就需要用到客戶端API,而客戶端API中最主要的就是WebSocket。
它提供了對websocket的功能封裝。它的構造函數是這樣的:
WebSocket(url[, protocols])
url就是要連接的websocket的地址,那么可選的protocols是什么呢?protocols可以傳入單個協議字符串或者是協議字符串數組。它指的是 WebSocket 服務器實現的子協議。
子協議是在WebSocket協議基礎上發展出來的協議,主要用於具體的場景的處理,它是是在WebSocket協議之上,建立的更加嚴格的規范。
比如,客戶端請求服務器時候,會將對應的協議放在Sec-WebSocket-Protocol頭中:
GET /socket HTTP/1.1
...
Sec-WebSocket-Protocol: soap, wamp
服務器端會根據支持的類型,做對應的返回,如:
Sec-WebSocket-Protocol: soap
WebSocket API有四種狀態,分別是:
狀態定義 | 取值 |
---|---|
WebSocket.CONNECTING | 0 |
WebSocket.OPEN | 1 |
WebSocket.CLOSING | 2 |
WebSocket.CLOSED | 3 |
通過調用close或者Send方法,會觸發相應的events事件,WebSocket API 的事件主要有:close,error,message,open這4種。
下面是一個具體使用的例子:
// 創建連接
const socket = new WebSocket('ws://localhost:8000');
// 開啟連接
socket.addEventListener('open', function (event) {
socket.send('沒錯,開啟了!');
});
// 監聽消息
socket.addEventListener('message', function (event) {
console.log('監聽到服務器的消息 ', event.data);
});
總結
以上就是websocket的簡單介紹和使用,有想知道Websocket到底是怎么進行消息傳輸的,敬請期待我的下一篇文章。
本文已收錄於 http://www.flydean.com/06-websocket/
最通俗的解讀,最深刻的干貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!
歡迎關注我的公眾號:「程序那些事」,懂技術,更懂你!