chanshiyucx
4/22/2019 - 11:01 AM

websocket 心跳重连

通过心跳进行 websocket 重连

export default class WebsocketHB {
  constructor({
    url,
    pingTimeout = 8000, // 发送心跳包间隔,默认 8000 毫秒
    pongTimeout = 15000, // 最长未接收消息的间隔,默认 15000 毫秒
    reconnectTimeout = 5000, // 每次重连间隔
    reconnectLimit = 15, // 最大重连次数
    pingMsg // 心跳包的消息内容
  }) {
    // 初始化配置
    this.url = url
    this.pingTimeout = pingTimeout
    this.pongTimeout = pongTimeout
    this.reconnectTimeout = reconnectTimeout
    this.reconnectLimit = reconnectLimit
    this.pingMsg = pingMsg

    // 实例变量
    this.ws = null
    this.pingTimer = null // 心跳包定时器
    this.pongTimer = null // 接收消息定时器
    this.reconnectTimer = null // 重连定时器
    this.reconnectCount = 0 // 当前的重连次数
    this.forbidReconnect = false // 禁止重连
    this.lockReconnect = false // 锁定重连
    this.lockReconnectTask = false // 锁定重连任务队列

    this.createWebSocket()
  }

  // 创建 WS
  createWebSocket() {
    try {
      this.ws = new WebSocket(this.url)
      this.ws.onclose = () => {
        this.onclose()
        this.readyReconnect()
      }
      this.ws.onerror = () => {
        this.onerror()
        this.readyReconnect()
      }
      this.ws.onopen = () => {
        this.onopen()
        this.resetStatus()
      }
      this.ws.onmessage = event => {
        this.onmessage(event)

        // 超时定时器
        clearTimeout(this.pongTimer)
        this.pongTimer = setTimeout(() => {
          this.readyReconnect()
        }, this.pongTimeout)
      }
    } catch (error) {
      console.error('websocket 连接失败!', error)
      throw error
    }
  }

  // 连接成功
  resetStatus() {
    this.clearAllTimer()
    this.heartBeat()
    this.reconnectCount = 0
    this.lockReconnect = false
    this.lockReconnectTask = false
  }

  // 准备重连
  readyReconnect() {
    // 避免循环重连,当一个重连任务进行时,不进行重连
    if (this.lockReconnectTask) return
    this.lockReconnectTask = true
    this.clearAllTimer()
    this.reconnect()
  }

  // 重连
  reconnect() {
    console.log('重连', this.reconnectCount)
    if (this.forbidReconnect) return
    if (this.lockReconnect) return
    if (this.reconnectCount > this.reconnectLimit) return

    this.lockReconnect = true
    this.reconnectCount += 1
    this.createWebSocket()
    this.reconnectTimer = setTimeout(() => {
      this.lockReconnect = false
      this.reconnect()
    }, this.reconnectTimeout)
  }

  // 发送心跳包
  heartBeat() {
    this.pingTimer = setTimeout(() => {
      this.send(this.pingMsg)
      this.heartBeat()
    }, this.pingTimeout)
  }

  // 发送消息
  send(msg) {
    this.ws.send(msg)
  }

  // 清空所有定时器
  clearAllTimer() {
    clearTimeout(this.pingTimer)
    clearTimeout(this.pongTimer)
    clearTimeout(this.reconnectTimer)
  }

  // 销毁 ws
  destroyed() {
    // 如果手动关闭连接,不再重连
    this.forbidReconnect = true
    this.clearAllTimer()
    this.ws && this.ws.close()
  }
}