对 websocket 进行封装 (心跳检测 断开重连 发送事件等等 支持断开重连后发送上次发送的最后一个事件)

代码封装: 

 

// websocketService.js
import { ref } from "vue";

const { DEV, VITE_APP_PUSH_WS } = import.meta.env;
const { push_ws } = window.WEB_CONFIG;
const baseWsUrl = DEV ? VITE_APP_PUSH_WS : push_ws;

class WebSocketService {
  constructor(url) {
    this.url = url;
    this.socket = null;
    this.reconnectTimer = null;
    this.heartbeatTimer = null;
    this.socketStatus = false;
    this.reconnectInterval = 5000; // 重连间隔时间
    this.heartbeatInterval = 30000; // 心跳间隔时间
    this.websocketObj = ref(null);
    this.onOpenCallback = null; // 新增用于保存连接成功后的回调函数
    this.eventListeners = {}; // 用于存储事件监听器
    this.lastSentMessage = null; // 存储最后发送的消息
  }

  initSocket(onOpenCallback) {
    this.socket = new WebSocket(this.url);

    this.socket.onopen = () => {
      clearInterval(this.reconnectTimer);
      this.socketStatus = true;
      this.startHeartbeat();
      this.websocketObj.value = this.socket;
      if (onOpenCallback) {
        onOpenCallback();
      }
      if (this.lastSentMessage) {
        this.sendMessage(this.lastSentMessage); // 重连后发送最后一条消息
      }
    };

    this.socket.onmessage = (event) => {
      const data = JSON.parse(event.data);
      const token = window.localStorage.getItem('token') || '';
      if (!token) {
        this.closeSocket();
      }
      if (this.eventListeners[data.event]) {
        this.eventListeners[data.event].forEach(callback => callback(data));
      }
    };

    this.socket.onclose = () => {
      this.handleSocketClose();
    };

    this.socket.onerror = () => {
      this.handleSocketError();
    };
  }

  startHeartbeat() {
    this.heartbeatTimer = setInterval(() => {
      this.socket.send(JSON.stringify({ event: "heartbeat" }));
    }, this.heartbeatInterval);
  }

  stopHeartbeat() {
    clearInterval(this.heartbeatTimer);
  }

  closeSocket() {
    this.stopHeartbeat();
    this.socket.close();
  }

  handleSocketClose() {
    console.log("WebSocket closed");
    this.socketStatus = false;
    this.stopHeartbeat();
    this.reconnect();
  }

  handleSocketError() {
    console.log("WebSocket error");
    this.socketStatus = false;
    this.stopHeartbeat();
    this.reconnect();
  }

  reconnect() {
    if (this.reconnectTimer) return;

    this.reconnectTimer = setInterval(() => {
      this.initSocket(this.onOpenCallback);
    }, this.reconnectInterval);
  }

  sendMessage(message) {
    this.lastSentMessage = message; // 存储最后发送的消息
    if (this.socket && this.socket.readyState === WebSocket.OPEN) {
      this.socket.send(message);
    } else {
      console.error('WebSocket is not open. Ready state is: ' + (this.socket ? this.socket.readyState : 'N/A'));
    }
  }

  addEventListener(event, callback) {
    if (!this.eventListeners[event]) {
      this.eventListeners[event] = [];
    }
    this.eventListeners[event].push(callback);
  }

  removeEventListener(event, callback) {
    if (this.eventListeners[event]) {
      this.eventListeners[event] = this.eventListeners[event].filter(cb => cb !== callback);
    }
  }
}

const wsUrl = `${baseWsUrl}/ws/footballMsg`;
const websocketService = new WebSocketService(wsUrl);
export const newwebsocketObj = websocketService.websocketObj;

export const initWebsocketObj = (onOpenCallback) => {
  websocketService.initSocket(onOpenCallback);
};

export const sendMessage = (message) => {
  websocketService.sendMessage(message);
};

export const closeWebsocket = () => {
  websocketService.closeSocket();
};

export const addEventListener = (event, callback) => {
  websocketService.addEventListener(event, callback);
};

export const removeEventListener = (event, callback) => {
  websocketService.removeEventListener(event, callback);
};

代码使用:

<script setup>
import { ref, onMounted, onUnmounted, watch } from 'vue';
import { initWebsocketObj, sendMessage, addEventListener, removeEventListener, websocketObj } from './websocketService.js';

const tableData = ref([]);
const standardMatchId = ref('match_id_example'); // 这是一个示例的 match_id,你可以从路由或其他地方获取

// 处理 WebSocket 消息的回调函数
const handleFootballMessageEvent = (data) => {
  if (data.event === 'FootballMessageEvent') {
    const { id = '' } = data.payload || {};
    if (!tableData.value.length || !id.startsWith(standardMatchId.value)) return;
    tableData.value.unshift(data.payload);
    tableData.value = [...new Set(tableData.value.map(JSON.stringify))].map(
      JSON.parse
    );
  }
};

onMounted(() => {
  // 初始化 WebSocket
  initWebsocketObj(() => {
    // 连接成功后发送初始消息
    const message = JSON.stringify({
      event: "subMatch",
      payload: { FootballMessageEvent: [standardMatchId.value] }
    });
    sendMessage(message);
  });

  // 添加事件监听器
  addEventListener('FootballMessageEvent', handleFootballMessageEvent);

  // 监听 tableData 的变化
  watch(tableData, (newValue) => {
    const standardMatchIds = newValue.map(item => item.standard_match_id + '');
    const params = {
      BetMarketEvent: standardMatchIds,
      FootballEvent: standardMatchIds
    };
    const message = JSON.stringify({ event: "subMatch", payload: params });
    sendMessage(message);
  });

  // 组件卸载时移除事件监听器
  onUnmounted(() => {
    removeEventListener('FootballMessageEvent', handleFootballMessageEvent);
    websocketObj.value && websocketObj.value.close();
  });
});
</script>

<template>
  <div>
    <table>
      <thead>
        <tr>
          <th>Match ID</th>
          <th>Event</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="item in tableData" :key="item.standard_match_id">
          <td>{{ item.standard_match_id }}</td>
          <td>{{ item.event }}</td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

  

posted @ 2024-06-19 15:32  1点  阅读(50)  评论(0编辑  收藏  举报