websocket可直接使用的示例

273 阅读3分钟

1001 界面刷新 1006 服务不稳定 1005 前端主动关闭的默认编码

①只有心跳和接收消息 ②只有接收消息类型后,在界面再去调用服务获取信息(后端这样设计的😇) ③根据ai的指导,用了指数重连机制

websocket封装

// WebSocketService.js

import dayjs from 'dayjs';

export default class WebSocketService {

  constructor(url) {

    this.wsuri = url;

    this.socket = null;

    this.lockReconnect = false;

    this.initialDelay = 1000; // 初始延迟时间,单位为毫秒

    this.maxDelay = 60 * 1000; // 最大延迟时间,单位为毫秒

    this.maxRetries = 20; // 最大重连次数

    this.retryCount = 0; // 当前重连次数

    this.callbacks = {}; // 用于存储回调函数

    // 心跳、心跳、心跳

    this.timeout = 10 * 1000

    this.timer = null

    this.serverTimer = null // 这个有值表示正在心跳

    this.isAlive = false // 连接标识,默认为 false

    this.isStart = false

  }

  /* 心跳 */

  heartReset () {

    console.log('-进行心跳断开-', dayjs().format('YYYY-MM-DD HH:mm:ss'));

    clearInterval(this.serverTimer)

    this.serverTimer = null // 心跳断开

    this.isAlive = false;

  }

  heartStart () {

    clearInterval(this.serverTimer)// 清除上次的定时器,防止重复开启

    this.serverTimer = null

    this.isAlive = true; // 状态设置为 true

    this.serverTimer = setInterval(() => { // 开始心跳

      if (this.isAlive === true) {

        console.log('发送 ping 心跳', dayjs().format('YYYY-MM-DD HH:mm:ss'));

        this.isAlive = false;

        this.socket.send('ping'); // 发送心跳包

      } else {

        console.log('-心跳已经断开-', dayjs().format('YYYY-MM-DD HH:mm:ss'));

        this.heartReset() // 如果心跳包没有收到响应,停止心跳

        this.socket.close(1005, '未收到心跳,重连'); // 断开重连

      }

    }, this.timeout);

  }

  /* 初始化 */

  initWebSocket () {

    if (!('WebSocket' in window)) {

      console.error('您的浏览器不支持 WebSocket', dayjs().format('YYYY-MM-DD HH:mm:ss'));

      return;

    }

    console.log(`WebSocket初始化中`, dayjs().format('YYYY-MM-DD HH:mm:ss'));

    this.socket = new WebSocket(this.wsuri);

    this.socket.onopen = this.onOpen.bind(this);

    this.socket.onerror = this.onError.bind(this);

    this.socket.onmessage = this.onMessage.bind(this);

    this.socket.onclose = this.onClose.bind(this);

  }

  


  onOpen () {

    console.log('WebSocket连接成功', dayjs().format('YYYY-MM-DD HH:mm:ss'));

    this.retryCount = 0; // 重置重试计数

    this.heartStart(); // 开启心跳

  }

  


  onError (error) {

    console.error('WebSocket连接发生错误', error, dayjs().format('YYYY-MM-DD HH:mm:ss'));

    this.onClose(error);

  }

  


  onMessage (event) {

    let message;

    try {

      if (event.data.includes('pong')) {

        console.log('收到 pong 响应', dayjs().format('YYYY-MM-DD HH:mm:ss'));

        this.isAlive = true; // 收到 pong,说明服务器正常

      } else {

        message = JSON.parse(event.data); // 尝试解析 JSON 格式数据

        const { messageType, data } = message;

        // 调用注册的回调函数

        if (this.callbacks[messageType]) {

          console.log('收到注册消息:', message, dayjs().format('YYYY-MM-DD HH:mm:ss'));

          this.callbacks[messageType](data);

        } else {

          console.warn('未注册的消息类型:', messageType, dayjs().format('YYYY-MM-DD HH:mm:ss'));

        }

      }

    } catch (e) {

      console.warn('收到非 JSON 格式消息:', event.data, event.data.includes('pong'), dayjs().format('YYYY-MM-DD HH:mm:ss'));

      // 调用非 JSON 数据的处理回调

      if (this.nonJsonCallback) {

        this.nonJsonCallback(event.data);

      } else {

        console.warn('未定义非 JSON 数据的处理回调', dayjs().format('YYYY-MM-DD HH:mm:ss'));

      }

    }

  }

  


  onClose (event) {

    console.log(`WebSocket连接关闭`, event, dayjs().format('YYYY-MM-DD HH:mm:ss'));

    this.heartReset(); // 关闭心跳

    if (event.code !== 1000) {

      this.reconnect(); // 重连

    }

  }

  


  reconnect () {

    if (this.retryCount < this.maxRetries) { // 如果小于最大重连次数

      const delay = Math.min(this.initialDelay * Math.pow(2, this.retryCount), this.maxDelay);

      console.log(`WebSocket开始第${this.retryCount + 1}次重连,(若失败,延迟${delay}ms后,执行下一次重连`, dayjs().format('YYYY-MM-DD HH:mm:ss'));

      setTimeout(this.initWebSocket(), delay); // 第一次直接重连,第二次等待1s,后面指数回避(重连次数增加,delay时间指数增加

      this.retryCount++;

    } else {

      console.error('Max retries reached, giving up.到达最大重连次数');

    }

  }

  


  sendMessage (data) {

    const payload = {

      timestamp: new Date().toISOString(),

      requestId: this.generateUUID(),

      messageType: data.messageType,

      data: data.data,

    };

    if (this.socket && this.socket.readyState === WebSocket.OPEN) {

      this.socket.send(JSON.stringify(payload));

    } else {

      console.error('WebSocket未连接,无法发送消息', dayjs().format('YYYY-MM-DD HH:mm:ss'));

    }

  }

  


  closeWebSocket () {

    if (this.socket) {

      this.socket.close(1000, '正常关闭');

    }

  }

  


  generateUUID () {

    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {

      const r = (Math.random() * 16) | 0;

      const v = c === 'x' ? r : (r & 0x3) | 0x8;

      return v.toString(16);

    });

  }

  


  // 注册回调函数

  registerCallback (messageType, callback) {

    if (typeof callback === 'function') {

      this.callbacks[messageType] = callback;

    } else {

      console.error('回调必须是函数');

    }

  }

  


  // 移除回调函数

  unregisterCallback (messageType) {

    delete this.callbacks[messageType];

  }

}

store使用

(之前看的时候有找到封装了的,看了不会用,要碎了)

// store/modules/websocket.js

import WebSocketService from '@/utils/websocket'

const { protocol, host } = location

export default {

  namespaced: true,

  state: {

    wsService: null, // WebSocket连接对象

    userId: ''// 连接的用户id

  },

  mutations: {

    // 设置WebSocket连接

    SET_SOCKET (state, socket) {

      state.wsService = socket;

    },

    SET_USER (state, userId) {

      state.userId = userId;

    },

  },

  actions: {

    // 初始化WebSocket连接

    initSocket ({ commit, dispatch, state, rootState }) {

      const userId = rootState.user.userInfo.userId;

      if (!userId) {

        return Promise.reject(new Error('用户id不存在'))

      }

      // 判断wsService是否存在。

      // 如果存在,判断用户id是否相同,如果相同是否已经连接。

      // 如果已连接判断心跳是否开启,如果关闭就开启;如果未连接则创建新连接

      if (state.wsService) {

        const wsService = state.wsService

        const socket = state.wsService.socket;

        if (state.userId === userId && (socket.readyState === WebSocket.OPEN || socket.readyState === WebSocket.CONNECTING)) {

          // CLOSED: 3

          // CLOSING: 2

          // CONNECTING: 0

          // OPEN: 1

          if (!wsService.serverTimer) {

            wsService.heartStart();

          }

        } else {

          dispatch('startSocket')

          return Promise.resolve()

        }

      } else {

        dispatch('startSocket')

        return Promise.resolve()

      }

    },

    startSocket ({ commit, dispatch, state, rootState }) {

      const userId = rootState.user.userInfo.userId;

      if (state.wsService) {

        state.wsService.closeWebSocket() // 正常关闭连接

      }

      // wsService进行初始化。        

      const wsService = new WebSocketService(

        `${protocol === 'https:' ? 'wss' : 'ws'}://${host}//${userId}`)

      wsService.initWebSocket()

      commit('SET_USER', userId)

      commit('SET_SOCKET', wsService)

    },

    closeSocket ({ commit, dispatch, state }, path) {

      if (state.wsService) {

        const pageArr = [''] // 需要开启websocket的页面

        let needClose = true

        pageArr.forEach(item => {

          if (path.includes(item)) {

            needClose = false

          }

        })

        if (needClose === true) {

          state.wsService.closeWebSocket() // 正常关闭连接

        }

      }

    },

  },

};