小學生都能讀懂的網絡協議之:WebSocket

簡介

服務端和客戶端應該怎麼進行通信呢?我們常見的方法就是客戶端向服務器端發送一個請求,然後服務器端向客戶端發送返回的響應。這種做法比較簡單,邏輯也很清晰,但是在某些情況下,這種操作方式並不好使。

比如在服務器端的某些變動需要通知客戶端的情況,因爲客戶端並不知道服務器端的變動是否完成,所以需要不停的使用輪循去檢測服務器的狀態。這種做法的缺點就是太過於浪費資源。如果希望及時性好的話,需要不斷的減少輪循的時間間隔,導致極大的服務器壓力和資源的浪費。

那麼有沒有好的解決辦法呢?

既然不能使用查詢,那麼就改成服務器推送就行了。我們知道在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/

最通俗的解讀,最深刻的乾貨,最簡潔的教程,衆多你不知道的小技巧等你來發現!

歡迎關注我的公衆號:「程序那些事」,懂技術,更懂你!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章