WebSocket 用法

1. 什么是WebSocket?

WebSocket是一種協(xié)議,用于在Web應用程序和服務器之間建立實時、雙向的通信連接。
它通過一個單一的TCP連接提供了持久化連接,這使得Web應用程序可以更加實時地傳遞數(shù)據(jù)。
WebSocket協(xié)議最初由W3C開發(fā),并于2011年成為標準。

2. WebSocket的優(yōu)勢和劣勢

WebSocket的優(yōu)勢包括:

實時性: 由于WebSocket的持久化連接,它可以實現(xiàn)實時的數(shù)據(jù)傳輸,避免了Web應用程序需要不斷地發(fā)送請求以獲取最新數(shù)據(jù)的情況。
雙向通信: WebSocket協(xié)議支持雙向通信,這意味著服務器可以主動向客戶端發(fā)送數(shù)據(jù),而不需要客戶端發(fā)送請求。
減少網(wǎng)絡負載: 由于WebSocket的持久化連接,它可以減少HTTP請求的數(shù)量,從而減少了網(wǎng)絡負載。
WebSocket的劣勢包括:

需要瀏覽器和服務器都支持: WebSocket是一種相對新的技術,需要瀏覽器和服務器都支持。一些舊的瀏覽器和服務器可能不支持WebSocket。
需要額外的開銷: WebSocket需要在服務器上維護長時間的連接,這需要額外的開銷,包括內(nèi)存和CPU。
安全問題: 由于WebSocket允許服務器主動向客戶端發(fā)送數(shù)據(jù),可能會存在安全問題。服務器必須保證只向合法的客戶端發(fā)送數(shù)據(jù)。

3. WebSocket的協(xié)議

WebSocket 協(xié)議是一種基于TCP的協(xié)議,用于在客戶端和服務器之間建立持久連接,并且可以在這個連接上實時地交換數(shù)據(jù)。WebSocket協(xié)議有自己的握手協(xié)議,用于建立連接,也有自己的數(shù)據(jù)傳輸格式。

當客戶端發(fā)送一個 WebSocket 請求時,服務器將發(fā)送一個協(xié)議響應以確認請求。在握手期間,客戶端和服務器將協(xié)商使用的協(xié)議版本、支持的子協(xié)議、支持的擴展選項等。一旦握手完成,連接將保持打開狀態(tài),客戶端和服務器就可以在連接上實時地傳遞數(shù)據(jù)。

WebSocket 協(xié)議使用的是雙向數(shù)據(jù)傳輸,即客戶端和服務器都可以在任意時間向?qū)Ψ桨l(fā)送數(shù)據(jù),而不需要等待對方的請求。它支持二進制數(shù)據(jù)和文本數(shù)據(jù),可以自由地在它們之間進行轉換。

總之,WebSocket協(xié)議是一種可靠的、高效的、雙向的、持久的通信協(xié)議,它適用于需要實時通信的Web應用程序,如在線游戲、實時聊天、儀表盤、股票行情等等。

4. WebSocket的生命周期

WebSocket 生命周期描述了 WebSocket 連接從創(chuàng)建到關閉的過程。一個 WebSocket 連接包含以下四個主要階段:

連接建立階段(Connection Establishment): 在這個階段,客戶端和服務器之間的 WebSocket 連接被建立。客戶端發(fā)送一個 WebSocket 握手請求,服務器響應一個握手響應,然后連接就被建立了。
連接開放階段(Connection Open): 在這個階段,WebSocket 連接已經(jīng)建立并開放,客戶端和服務器可以在連接上互相發(fā)送數(shù)據(jù)。
連接關閉階段(Connection Closing): 在這個階段,一個 WebSocket 連接即將被關閉。它可以被客戶端或服務器發(fā)起,通過發(fā)送一個關閉幀來關閉連接。
連接關閉完成階段(Connection Closed): 在這個階段,WebSocket 連接已經(jīng)完全關閉。客戶端和服務器之間的任何交互都將無效。
“需要注意的是,WebSocket 連接在任何時候都可能關閉,例如網(wǎng)絡故障、服務器崩潰等情況都可能導致連接關閉。因此,需要及時處理 WebSocket 連接關閉的事件,以確保應用程序的可靠性和穩(wěn)定性。

5.WebSocket的性能

1 與傳統(tǒng)的HTTP請求/響應模型比較

  • 雙向通信性能更好: WebSocket協(xié)議使用單一的TCP連接,允許客戶端和服務器在同一個連接上進行雙向通信。這種實時的雙向通信可以更快地傳輸數(shù)據(jù),而不需要建立多個HTTP請求/響應連接。
  • 更小的網(wǎng)絡流量: 與HTTP相比,WebSocket協(xié)議需要更少的網(wǎng)絡流量來維護連接,因為它不需要在每個請求/響應交換中發(fā)送頭部信息。
  • 更低的延遲: WebSocket協(xié)議允許服務器主動向客戶端推送消息,而不需要客戶端先發(fā)送請求。這種實時通信可以減少響應延遲,并提高應用程序的性能。
  • 更好的服務器資源管理: 由于WebSocket連接可以保持活動狀態(tài),服務器可以更好地管理客戶端連接,減少服務器開銷和處理時間。

WebSocket協(xié)議的性能比傳統(tǒng)的HTTP請求/響應模型更好,特別是在實時通信和低延遲方面。WebSocket協(xié)議適用于需要實時通信和實時數(shù)據(jù)更新的應用程序,如在線聊天、多人游戲、實時監(jiān)控等。

2 優(yōu)化WebSocket的性能

  • 減少消息大小: WebSocket 傳輸?shù)臄?shù)據(jù)大小對性能有很大影響。盡量減少消息的大小,可以降低網(wǎng)絡帶寬和服務器負載。例如,可以使用二進制傳輸協(xié)議來代替文本傳輸,或使用壓縮算法對消息進行壓縮。
  • 使用CDN加速: 使用 CDN可以將靜態(tài)資源緩存到離用戶更近的節(jié)點上,提高傳輸速度和性能。CDN 可以緩存 Websocket 的初始握手請求,避免不必要的網(wǎng)絡延遲。
  • 使用[負載均衡]** WebSocket 服務可以使用負載均衡來分配并平衡多個服務器的負載。負載均衡可以避免單個服務器被過載,并提高整個服務的可伸縮性。
  • 優(yōu)化服務端代碼: WebSocket 服務端代碼的性能也是關鍵因素。使用高效的框架和算法,避免使用過多的內(nèi)存和 CPU 資源,可以提高服務端的性能和響應速度。
  • 避免網(wǎng)絡阻塞: WebSocket 的性能也會受到網(wǎng)絡阻塞的影響。當有太多的連接同時請求數(shù)據(jù)時,服務器的性能會下降。使用合適的線程池和異步 IO 操作可以避免網(wǎng)絡阻塞,提高 WebSocket 服務的并發(fā)性能。

6. WebSocket的擴展應用和未來發(fā)展方向

  • 更加完善的標準規(guī)范: WebSocket 標準規(guī)范還有很多可以優(yōu)化的地方,未來可能會繼續(xù)完善 WebSocket 的標準規(guī)范,以適應更加復雜的應用場景。
  • 更加安全的通信方式: 由于 WebSocket 的開放性,使得它可能會受到一些安全威脅,未來可能會通過加密、[身份驗證] 等方式來增強 WebSocket 的安全性。
  • 更好的兼容性: WebSocket 協(xié)議需要在 HTTP 協(xié)議的基礎上建立連接,因此可能會遇到兼容性問題,未來可能會通過技術手段來解決這些問題。
  • 更好的性能和可伸縮性: WebSocket 協(xié)議的性能和可伸縮性對于復雜的應用場景非常關鍵,未來可能會通過技術手段來進一步提高 WebSocket 的性能和可伸縮性。

使用

 <global-web-socket  :uri="`/websocket/url1`" @getData="getData" />
// socket傳回數(shù)據(jù)
    getData(data) {
      try {
       console.log(data,'success');
      } catch (err) {
        console.log(err, 'socket');
      }
    },

組件

<template>

</template>
<script>
import store from '@/store';

export default {
  props: {
    uri: {
      type: String
    }
  },

  data() {
    return {
      // webSocket實例
      webSocket: null, 
      // 重連鎖,避免多次重連
      lockReconnect: false, 
      // 最大重連次數(shù), -1 標識無限重連
      maxReconnect: 8, 
      // 重連嘗試次數(shù)
      reconnectTime: 0, 
      heartbeat: {
        // 心跳間隔時間
        interval: 30 * 1000, 
        // 響應超時時間
        timeout: 10 * 1000, 
        // 延時發(fā)送心跳的定時器
        pingTimeoutObj: null, 
        // 接收心跳響應的定時器
        pongTimeoutObj: null, 
        // 心跳請求信息
        pingMessage: JSON.stringify({ type: 'ping' }) 
      }
    };
  },

  computed: {},

  created() {
    this.initWebSocket();
  },

  destroyed: function() {
    this.webSocket.close();
    this.clearTimeoutObj(this.heartbeat);
  },

  methods: {
    /**
     * 初始化 weoSocket
     */
    initWebSocket() {
      // ws地址
      const host = window.location.host;
      const wsUri = `ws://${host}${this.uri}?access_token=token`;
      // 建立連接
      this.webSocket = new WebSocket(wsUri);
      // 連接成功
      this.webSocket.onopen = this.onOpen;
      // 連接錯誤
      this.webSocket.onerror = this.onError;
      // 接收信息
      this.webSocket.onmessage = this.onMessage;
      // 連接關閉
      this.webSocket.onclose = this.onClose;
    },

    /**
     * 重新連接
     */
    reconnect() {
      if (!this.token) {
        return;
      }
      if (this.lockReconnect || (this.maxReconnect !== -1 && this.reconnectTime > this.maxReconnect)) {
        return;
      }
      this.lockReconnect = true;
      setTimeout(() => {
        this.reconnectTime++;
        // 建立新連接
        this.initWebSocket();
        this.lockReconnect = false;
      }, 5000);
    },

    /**
     * 清空定時器
     */
    clearTimeoutObj: function(heartbeat) {
      heartbeat.pingTimeoutObj && clearTimeout(heartbeat.pingTimeoutObj);
      heartbeat.pongTimeoutObj && clearTimeout(heartbeat.pongTimeoutObj);
    },

    /**
     * 開啟心跳
     */
    startHeartbeat() {
      const webSocket = this.webSocket;
      const heartbeat = this.heartbeat;
      // 清空定時器
      this.clearTimeoutObj(heartbeat);
      // 延時發(fā)送下一次心跳
      heartbeat.pingTimeoutObj = setTimeout(() => {
        // 如果連接正常
        if (webSocket.readyState === 1) {
          // 這里發(fā)送一個心跳,后端收到后,返回一個心跳消息,
          webSocket.send(heartbeat.pingMessage);
          // 心跳發(fā)送后,如果服務器超時未響應則斷開,如果響應了會被重置心跳定時器
          heartbeat.pongTimeoutObj = setTimeout(() => {
            webSocket.close();
          }, heartbeat.timeout);
        } else {
          // 否則重連
          this.reconnect();
        }
      }, heartbeat.interval);
    },

    /**
     * 連接成功事件
     */
    onOpen() {
      console.log('WebSocket connection success');
      // 開啟心跳
      this.startHeartbeat();
      this.reconnectTime = 0;
    },

    /**
     * 連接失敗事件
     * @param e
     */
    onError(e) {
      // 錯誤
      console.log(`WebSocket connection error:${e.code} ${e.reason} ${e.wasClean}`);
      // 重連
      this.reconnect();
    },

    /**
     * 連接關閉事件
     * @param e
     */
    onClose(e) {
      // 關閉
      console.log(`WebSocket connection closed:${e.code} ${e.reason} ${e.wasClean}`);
      // 重連
      this.reconnect();
    },

    /**
     * 接收服務器推送的信息
     * @param msgEvent
     */
    onMessage(msgEvent) {
      // 收到服務器信息,心跳重置并發(fā)送
      this.startHeartbeat();
      const text = msgEvent.data;
      if (text.indexOf('pong') > 0) {
        return;
      }

      this.$notify.warning({
        title: '消息通知',
        dangerouslyUseHTMLString: true,
        message: text + '消息通知',
        offset: 60
      });
    },

    /**
     * 數(shù)據(jù)發(fā)送
     * @param msg
     */
    send(msg) {
      // 數(shù)據(jù)發(fā)送
      this.webSocket.send(msg);
    }
  }
};

</script>
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容