WebSocket魔法師:打造實時應用的無限可能

2023-11-09 18:00:53

1、背景

在開發一些前端頁面的時候,總是能接收到這樣的需求:如何保持頁面並實現自動更新資料呢?以往的常規做法,是前端使用定時輪詢後端介面,獲取響應後重新渲染前端頁面,這種做法雖然能達到類似的效果,但是依然有很多缺點,缺點就不在這裡說了,感興趣的小夥伴可以自行查閱一下。現在讓我們回憶一下,我們有沒有想過,是否有一種技術,伺服器可以主動將資料推播給使用者端進行渲染,而不再是使用者端向伺服器發出請求等待返回結果呢?接下來,讓我們一起了解weboskcet。

2、什麼是websocket

websocket是HTML5規範的一個部分,它借鑑了socket的思想,實現了瀏覽器與伺服器全雙工通訊,達到了即時通訊的效果。websocket協定基於TCP協定實現,包含初始的握手過程,以及後續的多次資料框雙向傳輸過程,避免伺服器頻繁開啟多個HTTP連線,從而能更好的節省伺服器資源和頻寬,提高工作效率與資源利用率

3、工作原理

websocket的通訊規範,首先瀏覽器通過HTTP協定發出websocket的連線請求,伺服器進行響應,這個過程稱為握手,握手完成後,使用者端和伺服器之間建立一個類似TCP的連線,使用websocket協定,從而實現它們之間的通訊。

4、與HTTP的關係

相同點:

1、都是基於TCP協定,且都是可靠性傳輸協定;

2、都是應用層協定;

3、websocket支援兩種資源標誌符ws(預設80埠)與wss(預設443埠),類似HTTP和HTTPS;

不同點:

1、websocket是雙向通訊協定,HTTP是單向的;

2、websocket是需要瀏覽器和伺服器握手進行建立連線的,HTTP是瀏覽器發起向伺服器的連線,伺服器預先並不知道這個連線。

聯絡點:

websocket在建立握手時,資料是通過HTTP傳輸的,建立之後的資料傳輸將不再需要HTTP協定,而是websocket協定;

5、websocket建立與常用的屬性方法

5.1 websocket屬性

屬性 描述
readyState 唯讀屬性 readyState 表示連線狀態,可以是以下值:0 :表示連線尚未建立。1 :表示連線已建立,可以進行通訊。2 :表示連線正在進行關閉。3 :表示連線已經關閉或者連線不能開啟。
bufferedAmount 唯讀屬性 bufferedAmount 已被 send() 放入正在佇列中等待傳輸,但是還沒有發出的 UTF-8 文字位元組數。
CONNECTING 值為0,表示正在連線
OPEN 值為1,表示連線已建立,可以進行通訊
CLOSING 值為2,表示連線正在關閉
CLOSED 值為3,表示連線已經關閉或者連線不能開啟
// 建立websocket
var ws = new WebSocket("ws://www.example.com");
  if(ws.readyState == ws.CONNECTING){
    console.log('連線正在開啟');
  }

  ws.onopen = function () {
    ws.send(consumerId);
    if(ws.readyState == ws.CONNECTING){
      console.log('連線正在開啟1');
    }
    if(ws.readyState == ws.OPEN){
      console.log('連線已開啟');
    }
    console.log('已經建立連線');
    // 關閉連線
    // ws.close()
  };


  // 連線關閉時觸發
  ws.onclose = function () {
    if(ws.readyState == ws.CLOSED){
      console.log('連線已關閉')
    }
  };

  // 連線錯誤
  ws.onerror = function () {
    console.log('連線錯誤');
  };



5.2 weboscket事件

事件 處理程式 描述
open onopen 連線建立時觸發
message onmessage 接收訊息時觸發
error onerror 發生錯誤時觸發
close onclose 關閉連線時觸發

5.3 使用者端的簡單範例

// 建立websocket
var ws = new WebSocket("ws://www.example.com");

// 連線成功時觸發
ws.onopen = function(e) {
    console.log("Connectiong open ...");
    // 傳送訊息
    ws.send("Hello WebSocket");
};

// 接收訊息時觸發
ws.onmessage = function(e) {
    console.log("Received Message: " + e.data);
    ws.close();
};

// 關閉連線時觸發
ws.onclose = function(e) {
    console.log("Connection closed");
};

// 出現錯誤時觸發
ws.onerror = function(e) {
    console.log("error");
};



5.4 伺服器端的簡單範例

# 建立websocket伺服器端
from tornado.websocket import WebSocketHandler

class wsHandler(WebSocketHandler):
    # 儲存連線的使用者,用於後續推播訊息
    connect_users = set()
    
    # 已與使用者端建立連線
    def open(self):
        print("開啟WebSocket opened")
        self.connect_users.add(self)

    # 關閉使用者端連線
    def on_close(self):
        self.connect_users.remove(self)
    
    # 接收到訊息
    def on_message(self, message): 
        self.write_message("接收到使用者端的訊息:{}".format(message))

    # 所有使用者傳送訊息
    @classmethod
    def send_demand_updates(cls, message):
        # 使用@classmethod可以使類方法在呼叫的時候不用進行範例化
        # 給所有使用者推播訊息(此處可以根據需要,修改為給指定使用者進行推播訊息)
        for user in cls.connect_users:
            user.write_message(message)

    # 允許WebSocket的跨域請求
    def check_origin(self, origin):
        return True

if __name__ == "__main__":
    # 呼叫
    wsHandler。send_demand_updates("伺服器端傳送給使用者端的訊息")



注意:範例中使用python語言,需依託tornado框架搭建後端web伺服器端,文章中不再說明如何搭建伺服器端,感興趣的小夥伴可自行嘗試。tornado內建websocket模組,能更簡單的支援使用websocket。

6、總結

websocket提供了一種低延遲、高效能的雙向資料通訊,不同與web開發的請求、處理、等待響應模式,它是使用者端、伺服器端因為同一個連線直接就可以資料互傳的模式,特別適合實時資料互動的應用進行開發。

實用點:

1、websocket連線建立後,後續的資料傳輸都將以幀序列的形式傳輸;

2、在使用者端斷開websocket連線或伺服器端中斷連線前,不需要使用者端和伺服器端重新發起連線請求;

3、在海量並行、使用者端與伺服器互動負載流量大的情況下,節省網路頻寬資源的消耗,且使用者端傳送與接收訊息,都是在同一個持久連線上進行,實現了「真長連線」,真正的實現即時通訊。

作者:京東物流 駱銅磊

來源:京東雲開發者社群 自猿其說Tech 轉載請註明來源