WebSocket 從入門到入土

前言

因新部門需求有一個後臺管理需要一個右上角的實時的消息提醒功能,第一時間想到的就是使用 WebSocket 建立實時通信了,之前沒整過,於是只能學習了。和原部門相比現在太忙了,快樂的日子一去不復返了。經典的加量不加薪啊!!!

一. WebSocket 基本概念

1.WebSocket 是什麼?

WebSocket 是基於 TCP 的一種新的應用層網絡協議。它提供了一個全雙工的通道,允許服務器和客戶端之間實時雙向通信。因此,在 WebSocket 中,瀏覽器和服務器只需要完成一次握手,兩者之間就直接可以創建持久性的連接,並進行雙向數據傳輸,客戶端和服務器之間的數據交換變得更加簡單。WebSocket

2. 與 HTTP 協議的區別

與 HTTP 協議相比,WebSocket 具有以下優點:

  1. 更高的實時性能:WebSocket 允許服務器和客戶端之間實時雙向通信,從而提高了實時通信場景中的性能。

  2. 更少的網絡開銷:HTTP 請求和響應之間需要額外的數據傳輸,而 WebSocket 通過在同一個連接上雙向通信,減少了網絡開銷。

  3. 更靈活的通信方式:HTTP 請求和響應通常是一一對應的,而 WebSocket 允許服務器和客戶端之間以多種方式進行通信,例如消息 Push、事件推送等。

  4. 更簡潔的 API:WebSocket 提供了簡潔的 API,使得客戶端開發人員可以更輕鬆地進行實時通信。

當然肯定有缺點的:

  1. 不支持無連接: WebSocket 是一種持久化的協議,這意味着連接不會在一次請求之後立即斷開。這是有利的,因爲它消除了建立連接的開銷,但是也可能導致一些資源泄漏的問題。

  2. 不支持廣泛: WebSocket 是 HTML5 中的一種標準協議,雖然現代瀏覽器都支持,但是一些舊的瀏覽器可能不支持 WebSocket。

  3. 需要特殊的服務器支持: WebSocket 需要服務端支持,只有特定的服務器才能夠實現 WebSocket 協議。這可能會增加系統的複雜性和部署的難度。

  4. 數據流不兼容: WebSocket 的數據流格式與 HTTP 不同,這意味着在不同的網絡環境下,WebSocket 的表現可能會有所不同。

3.WebSocket 工作原理

1.  握手階段

WebSocket 在建立連接時需要進行握手階段。握手階段包括以下幾個步驟:

2.  數據傳輸階段

建立連接後,客戶端和服務端就可以通過 WebSocket 進行實時雙向通信。數據傳輸階段包括以下幾個步驟:

雙方如何進行相互傳輸數據的 具體的數據格式是怎麼樣的呢?WebSocket 的每條消息可能會被切分成多個數據幀(最小單位)。發送端會將消息切割成多個幀發送給接收端,接收端接收消息幀,並將關聯的幀重新組裝成完整的消息。

發送方 -> 接收方:ping。

接收方 -> 發送方:pong。

ping 、pong 的操作,對應的是 WebSocket 的兩個控制幀

3.  關閉階段

當不再需要 WebSocket 連接時,需要進行關閉階段。關閉階段包括以下幾個步驟:

總的來說,WebSocket 通過握手階段、數據傳輸階段和關閉階段實現了服務器和客戶端之間的實時雙向通信。

二. WebSocket 數據幀結構和控制幀結構。

1.  數據幀結構

WebSocket 數據幀主要包括兩個部分:幀頭和有效載荷。以下是 WebSocket 數據幀結構的簡要介紹:

2.  控制幀結構

除了數據幀之外,WebSocket 協議還包括一些控制幀,主要包括 Ping、Pong 和 Close 幀。以下是 WebSocket 控制幀結構的簡要介紹:

三. JavaScript 中 WebSocket 對象的屬性和方法,以及如何創建和連接 WebSocket。

WebSocket 對象的屬性和方法:

  1. WebSocket 對象:WebSocket 對象表示一個新的 WebSocket 連接。

  2. WebSocket.onopen 事件處理程序:當 WebSocket 連接打開時觸發。

  3. WebSocket.onmessage 事件處理程序:當接收到來自 WebSocket 的消息時觸發。

  4. WebSocket.onerror 事件處理程序:當 WebSocket 發生錯誤時觸發。

  5. WebSocket.onclose 事件處理程序:當 WebSocket 連接關閉時觸發。

  6. WebSocket.send 方法:向 WebSocket 發送數據。

  7. WebSocket.close 方法:關閉 WebSocket 連接。

創建和連接 WebSocket:

  1. 創建 WebSocket 對象:
javascript
複製代碼var socket = new WebSocket('ws://example.com');

其中,ws://example.com 是 WebSocket 的 URL,表示要連接的服務器。

  1. 連接 WebSocket:

使用 WebSocket.onopen 事件處理程序檢查 WebSocket 是否成功連接。

javascript
複製代碼socket.onopen = function() {
    console.log('WebSocket connected');
};
  1. 接收來自 WebSocket 的消息:

使用 WebSocket.onmessage 事件處理程序接收來自 WebSocket 的消息。

javascript
複製代碼socket.onmessage = function(event) {
    console.log('WebSocket message:', event.data);
};
  1. 向 WebSocket 發送消息:

使用 WebSocket.send 方法向 WebSocket 發送消息。

javascript
複製代碼socket.send('Hello, WebSocket!');
  1. 關閉 WebSocket:

當需要關閉 WebSocket 時,使用 WebSocket.close 方法。

javascript
複製代碼socket.close();

注意:在 WebSocket 連接成功打開和關閉時,會分別觸發 WebSocket.onopenWebSocket.onclose 事件。在接收到來自 WebSocket 的消息時,會觸發 WebSocket.onmessage 事件。當 WebSocket 發生錯誤時,會觸發 WebSocket.onerror 事件。

四. webSocket 簡單示例

以下是一個簡單的 WebSocket 編程示例,通過 WebSocket 向服務器發送數據,並接收服務器返回的數據:

  1. 首先,創建一個 HTML 文件,添加一個按鈕和一個用於顯示消息的文本框:
html
複製代碼<!DOCTYPE html>
<html>
<head>
   <meta charset="UTF-8">
   <title>WebSocket 示例</title>
</head>
<body>
   <button id="sendBtn">發送消息</button>
   <textarea id="messageBox" readonly></textarea>
   <script src="main.js"></script>
</body>
</html>
  1. 接下來,創建一個 JavaScript 文件(例如 main.js),並在其中編寫以下代碼:
javascript
複製代碼// 獲取按鈕和文本框元素
const sendBtn = document.getElementById('sendBtn');
const messageBox = document.getElementById('messageBox');

// 創建 WebSocket 對象
const socket = new WebSocket('ws://echo.websocket.org'); // 使用一個 WebSocket 服務器進行測試

// 設置 WebSocket 連接打開時的回調函數
socket.onopen = function() {
   console.log('WebSocket 連接已打開');
};

// 設置 WebSocket 接收到消息時的回調函數
socket.onmessage = function(event) {
   console.log('WebSocket 接收到消息:', event.data);
   messageBox.value += event.data + '\n';
};

// 設置 WebSocket 發生錯誤時的回調函數
socket.onerror = function() {
   console.log('WebSocket 發生錯誤');
};

// 設置 WebSocket 連接關閉時的回調函數
socket.onclose = function() {
   console.log('WebSocket 連接已關閉');
};

// 點擊按鈕時發送消息
sendBtn.onclick = function() {
   const message = 'Hello, WebSocket!';
   socket.send(message);
   messageBox.value += '發送消息: ' + message + '\n';
};

五. webSocket 應用場景

  1. 實時通信:WebSocket 非常適合實時通信場景,例如聊天室、在線遊戲、實時數據傳輸等。通過 WebSocket,客戶端和服務器之間可以實時通信,無需依賴輪詢,從而提高通信效率和減少網絡延遲。

  2. 監控數據傳輸:WebSocket 可以在監控系統中實現實時數據傳輸,例如通過 WebSocket,客戶端可以實時接收和處理監控數據,而無需等待輪詢數據。

  3. 自動化控制:WebSocket 可以在自動化系統中實現遠程控制,例如通過 WebSocket,客戶端可以遠程控制設備或系統,而無需直接操作。

  4. 數據分析:WebSocket 可以在數據分析場景中實現實時數據傳輸和處理,例如通過 WebSocket,客戶端可以實時接收和處理數據,而無需等待數據存儲和分析。

  5. 人工智能:WebSocket 可以在人工智能場景中實現實時數據傳輸和處理,例如通過 WebSocket,客戶端可以實時接收和處理數據,而無需等待數據處理和分析。

六. WebSocket 錯誤處理

WebSocket 的錯誤處理

  1. WebSocket is not supported:當瀏覽器不支持 WebSocket 時,會出現此錯誤。解決方法是在瀏覽器兼容性列表中檢查是否支持 WebSocket。

  2. WebSocket connection closed:當 WebSocket 連接被關閉時,會出現此錯誤。解決方法是在 WebSocket.onclose 事件處理程序中進行錯誤處理。

  3. WebSocket error:當 WebSocket 發生錯誤時,會出現此錯誤。解決方法是在 WebSocket.onerror 事件處理程序中進行錯誤處理。

  4. WebSocket timeout:當 WebSocket 連接超時時,會出現此錯誤。解決方法是在 WebSocket.ontimeout 事件處理程序中進行錯誤處理。

  5. WebSocket handshake error:當 WebSocket 握手失敗時,會出現此錯誤。解決方法是在 WebSocket.onerror 事件處理程序中進行錯誤處理。

  6. WebSocket closed by server:當 WebSocket 連接被服務器關閉時,會出現此錯誤。解決方法是在 WebSocket.onclose 事件處理程序中進行錯誤處理。

  7. WebSocket closed by protocol:當 WebSocket 連接被協議錯誤關閉時,會出現此錯誤。解決方法是在 WebSocket.onclose 事件處理程序中進行錯誤處理。

  8. WebSocket closed by network:當 WebSocket 連接被網絡錯誤關閉時,會出現此錯誤。解決方法是在 WebSocket.onclose 事件處理程序中進行錯誤處理。

  9. WebSocket closed by server:當 WebSocket 連接被服務器錯誤關閉時,會出現此錯誤。解決方法是在 WebSocket.onclose 事件處理程序中進行錯誤處理。

通過爲 WebSocket 對象的 oncloseonerrorontimeout 事件添加處理程序,可以及時捕獲和處理 WebSocket 錯誤,從而確保程序的穩定性和可靠性。

七. 利用單例模式創建完整的 wesocket 連接

js
複製代碼class webSocketClass {
    constructor(thatVue) {
      this.lockReconnect = false;
      this.localUrl = process.env.NODE_ENV === 'production' ? 你的websocket生產地址' : '測試地址';
      this.globalCallback = null;
      this.userClose = false;
      this.createWebSocket();
      this.webSocketState = false
      this.thatVue = thatVue
    }
  
    createWebSocket() {
      let that = this;
      // console.log('開始創建websocket新的實例', new Date().toLocaleString())
      if( typeof(WebSocket) != "function" ) {
        alert("您的瀏覽器不支持Websocket通信協議,請更換瀏覽器爲Chrome或者Firefox再次使用!")
      }
      try {
        that.ws = new WebSocket(that.localUrl);
        that.initEventHandle();
        that.startHeartBeat()
      } catch (e) {
        that.reconnect();
      }
    }

    //初始化
    initEventHandle() {
      let that = this;
      // //連接成功建立後響應
      that.ws.onopen = function() {
        console.log("連接成功");
      }; 
      //連接關閉後響應
      that.ws.onclose = function() {
        // console.log('websocket連接斷開', new Date().toLocaleString())
        if (!that.userClose) {
          that.reconnect(); //重連
        }
      };
      that.ws.onerror = function() {
        // console.log('websocket連接發生錯誤', new Date().toLocaleString())
        if (!that.userClose) {
          that.reconnect(); //重連
        }
      };
      that.ws.onmessage = function(event) {
        that.getWebSocketMsg(that.globalCallback);
        // console.log('socket server return '+ event.data);
      };
    }
    startHeartBeat () {
      // console.log('心跳開始建立', new Date().toLocaleString())
      setTimeout(() => {
          let params = {
            request: 'ping',
          }
          this.webSocketSendMsg(JSON.stringify(params))
          this.waitingServer()
      }, 30000)
    }
    //延時等待服務端響應,通過webSocketState判斷是否連線成功
    waitingServer () {
      this.webSocketState = false//在線狀態
      setTimeout(() => {
          if(this.webSocketState) {
              this.startHeartBeat()
              return
          }
          // console.log('心跳無響應,已斷線', new Date().toLocaleString())
          try {
            this.closeSocket()
          } catch(e) {
            console.log('連接已關閉,無需關閉', new Date().toLocaleString())
          }
          this.reconnect()
          //重連操作
      }, 5000)
    }
    reconnect() {
      let that = this;
      if (that.lockReconnect) return;
      that.lockReconnect = true; //沒連接上會一直重連,設置延遲避免請求過多
      setTimeout(function() {
        that.createWebSocket();
        that.thatVue.openSuccess(that) //重連之後做一些事情
        that.thatVue.getSocketMsg(that)
        that.lockReconnect = false;
      }, 15000);
    }
  
    webSocketSendMsg(msg) {
      this.ws.send(msg);
    }
  
    getWebSocketMsg(callback) {
      this.ws.onmessage = ev ={
        callback && callback(ev);
      };
    }
    onopenSuccess(callback) {
      this.ws.onopen = () ={
        // console.log("連接成功", new Date().toLocaleString())
        callback && callback()
      }
    }
    closeSocket() {
      let that = this;
      if (that.ws) {
        that.userClose = true;
        that.ws.close();
      }
    }
  }
  export default webSocketClass;

作者:耀耀切克鬧灬 

鏈接:https://juejin.cn/post/7309687967063818292

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/B1YaeuCXrCrq_hJaGSqmgQ