websocket 重连封装

/**
 * websocket 类
 *
 * 回调方法和原生websocket保持一致
 * @param {String} url 连接地址
 * @param {Boolean} isReconnect 是否包含重连机制
 *    定时向后端发送 'ping' 后端收到后返回对应值,以此检测连接是否在线
 *    如不在线则定时发起重连
 * @param {Object} options 配置参数
 * **/
class Ws {
  constructor(url, isReconnect = true, options = {}) {
    if (url) {
      if (typeof options === 'object') {
        this.opt = {
          url: url, // 连接地址
          // 心跳检测
          isReconnect: isReconnect, // 是否开启重连
          timeout: 4000, // 超时时间,超过后则发起重连
          pingString: 'ping', // 检测标识
          intervalStep: 3000, // 定时ping服务时间
          reconnectStep: 3000 // 设置重连间隔,防止重复连接
        }
        this.opt = {
          ...this.opt,
          ...options
        }
        this.lockReconnect = false // 重连锁,避免频繁重连
        this.isClose = false
        this.onopen = () => {} // 创建打开连接回调
        this.onmessage = () => {} // 收到消息通知的回调
        this.onclose = () => {} // 关闭连接的回调
        this.onerror = () => {} // 连接错误回调
        this.timeoutObj = null // 心跳检测计时器
        this.intervalObj = null // 定时发送计时器
        this.reconnectTimer = null // 避免重连锁
        this.createWs()
      } else {
        console.error('options need object')
      }
    } else {
      console.error('ur is required')
    }
  }
  // 创建ws
  createWs() {
    if (this.ws) {
      this.close()
    }
    this.ws = new WebSocket(this.opt.url)
    this.isClose = false
    this.setWsData()
    this.ws.onopen = () => {
      if (this.opt.isReconnect) {
        this.ping()
        this.reset().start()
      }
      this.onopen()
    }
    this.ws.onmessage = (data) => {
      if (this.opt.isReconnect && data.data === this.opt.pingString) {
        this.reset().start()
      } else {
        this.onmessage(data)
      }
    }
    this.ws.onclose = () => {
      if (this.opt.isReconnect) this.reconnect()
      this.onclose()
    }
    this.ws.onerror = () => {
      if (this.opt.isReconnect) this.reconnect()
      this.onerror()
    }
  }
  // 设置ws参数
  setWsData() {
    if (this.ws) {
      this.binaryType = this.ws.binaryType
      this.bufferedAmount = this.ws.bufferedAmount
      this.protocol = this.ws.protocol
      this.readyState = this.ws.protocol
    } else {
      this.binaryType = ''
      this.bufferedAmount = null
      this.protocol = ''
      this.readyState = null
    }
  }
  // 发送消息
  send(data) {
    this.ws.send(data)
    return this
  }
  // 关闭连接
  close() {
    this.isClose = true
    this.ws.close()
    return this
  }
  // 清除定时器
  clearTimer() {
    if (this.reconnectTimer) {
      clearTimeout(this.reconnectTimer)
    }
    if (this.timeoutObj) {
      clearTimeout(this.timeoutObj)
    }
    if (this.intervalObj) {
      clearTimeout(this.intervalObj)
    }
  }
  // 开启心跳
  start() {
    this.timeoutObj = setTimeout(() => {
      this.reconnect()
    }, this.opt.timeout)
    return this
  }
  // 重置心跳
  reset() {
    clearTimeout(this.timeoutObj)
    return this
  }
  // 重连
  reconnect() {
    if (this.isClose) {
      this.clearTimer()
      return
    }
    if (this.lockReconnect) return
    this.lockReconnect = true
    if (this.reconnectTimer) clearTimeout(this.reconnectTimer)
    this.reconnectTimer = setTimeout(() => {
      this.createWs()
      this.lockReconnect = false
    }, this.opt.reconnectStep)
    return this
  }
  // ping
  ping() {
    if (this.opt.isReconnect) {
      if (this.intervalObj) clearTimeout(this.intervalObj)
      this.intervalObj = setTimeout(() => {
        if (this.isClose) {
          this.clearTimer()
        } else {
          try {
            if (this.ws) {
              this.ws.send(this.opt.pingString)
              this.ping()
            }
          } catch (e) {
            console.info('ws ping error:', e)
          }
        }
      }, this.opt.intervalStep)
    }
  }
}
posted @ 2020-09-18 17:22  东庄  阅读(218)  评论(0编辑  收藏  举报