一个心跳机制+断线重连的的WebSocketManager

# 实现思路

  • 心跳机制,只要 websocket 连接成功,就会开启心跳

    • 用定时器循环发送心跳包消息给后端,消息内容{type: 'ping', content: timestamp }

    • 后端处理这个消息,并且返回同样的消息{type: 'ping', content: timestamp }给前端,注意 timestamp 要前后端保持一致,确保是对应的心跳包,如果前端发现超时了,说明连接已经断开,这时候会进行重连

  • 重连机制,重启只有两个情况触发,一个是非正常关闭的时候,一个是获取心跳包返回消息超时了

    • 非正常关闭,主动关闭的时候,设置关闭码为 1000;比如组件卸载、页面关闭、手动关闭的连接 的时候,这些情况下都手动调用closeWebSocket方法,其他情况都是非正常关闭,触发重连

    • 心跳包超时,前端发送完心跳包之后,一定时间内没有收到后端返回的对应心跳包消息,即认为连接断开,这个时候,触发重连

# 实现代码

点击 展开/收起 完整 代码
/**
 * @des 封装一个 心跳机制 和 断线重连的 WebSocketManager
 */

class WebSocketManager {
  constructor(url, options = {}) {
    this.url = url;
    this.socket = null;
    // 心跳定时器
    this.heartBeatInterval = null;
    // 断线重连定时器
    this.reconnectInterval = null;
    this.options = {
      reconnectDelay: options.reconnectDelay || 5000, // 断线重连的延迟
      heartBeatDelay: options.heartBeatDelay || 3000, // 心跳机制的间隔
      onOpen: options.onOpen || function () {}, // 连接成功的回调
      onMessage: options.onMessage || function () {}, // 接收消息的回调
      onError: options.onError || function () {}, // 连接错误的回调
      onClose: options.onClose || function () {}, // 连接关闭的回调
      onReconnect: options.onReconnect || function () {}, // 重连成功的回调
    };
  }

  // 初始化连接
  initWebSocket() {
    // 如果 socket 存在 或者 正在连接, 直接返回
    if (this.isOnline() || this.isOnConnecting()) {
      return;
    }

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

    this.socket.onopen = () => {
      // 确保成功连接后停止重连逻辑
      this.stopReconnect();
      // 连接成功回调
      this.options.onOpen();
      // 开启心跳
      this.startHeartBeat();
    };

    this.socket.onmessage = (event) => {
      this.options.onMessage(event);
    };

    this.socket.onerror = (error) => {
      this.options.onError(error);
    };

    this.socket.onclose = (event) => {
      this.options.onClose(event);
      if (event.code !== 1000) {
        // 1000 表示正常关闭,非正常关闭时重连
        this.reconnect();
      }
    };
  }

  reconnect() {
    // 如果 已经存在socket 或者 正在重连,则直接返回
    if (this.reconnectInterval || this.isOnline() || this.isOnConnecting()) {
      return;
    }

    this.reconnectInterval = setInterval(() => {
      // 如果 已经连接,关闭重连计时器,然后返回
      if (this.isOnline()) {
        this.stopReconnect();
        return;
      }

      // 如果 正在连接,直接返回
      if (this.isOnConnecting()) {
        return;
      }

      this.options.onReconnect();
      this.initWebSocket();
    }, this.options.reconnectDelay);
  }

  // 开始心跳
  startHeartBeat() {
    this.heartBeatInterval = setInterval(() => {
      // 如果socket存在且连接成功,发送心跳包
      if (this.isOnline()) {
        this.socket.send(JSON.stringify({ type: 'ping' }));
      }
    }, this.options.heartBeatDelay);
  }

  // 停止心跳
  stopHeartBeat() {
    clearInterval(this.heartBeatInterval);
    this.heartBeatInterval = null;
  }

  // 停止重连
  stopReconnect() {
    if (this.reconnectInterval) {
      clearInterval(this.reconnectInterval);
      this.reconnectInterval = null;
    }
  }

  // 手动关闭 WebSocket
  closeWebSocket() {
    if (this.isOnline() || this.isOnConnecting()) {
      this.stopHeartBeat();
      this.stopReconnect();
      this.socket.close(1000, '正常关闭');
    }
  }

  // 发送消息
  sendMessage(message) {
    if (this.isOnline()) {
      this.socket.send(message);
    }
  }

  // 当前连接器的状态
  getReadyState() {
    if (this.socket) {
      return this.socket.readyState;
    }
    return -1;
  }

  // 判断 websocket 是否在线
  isOnline() {
    if (this.socket) {
      return this.socket.readyState === WebSocket.OPEN;
    }
    return false;
  }

  // 判断 websocket 是否正在连接
  isOnConnecting() {
    if (this.socket) {
      return this.socket.readyState === WebSocket.CONNECTING;
    }
    return false;
  }
}

export default WebSocketManager;