前端websocket 心跳检测

218 阅读2分钟

背景

websocket是前后端交互的长连接,前后端也都可能因为一些情况导致连接失效并且相互之间没有反馈提醒。因此为了保证连接的可持续性和稳定性,websocket心跳重连就应运而生。

网络断开

在使用原生websocket的时候,如果设备网络断开,不会立刻触发websocket的任何事件,前端也就无法得知当前连接是否已经断开。这个时候如果调用websocket.send方法,浏览器才会发现链接断开了,便会立刻或者一定短时间后(不同浏览器或者浏览器版本可能表现不同)触发onclose函数。

后端websocket异常

后端websocket服务也可能出现异常,造成连接断开,这时前端也并没有收到断开通知,因此需要前端定时发送心跳消息ping,后端收到ping类型的消息,立马返回ping消息,告知前端连接正常。如果一定时间没收到ping消息,就说明连接不正常,前端便会执行重连。

为了解决以上两个问题,以前端作为主动方,定时发送ping消息,用于检测网络和前后端连接问题。一旦发现异常,前端持续执行重连逻辑,直到重连成功。

heartbeat.png

代码实现


interface OptionsParams {
    heartbeat: boolean;
    timeout?: number | undefined;

}
export default class Websocket {
    options: OptionsParams;
    websocket: WebSocket | null = null;
    reconnectTimeOut: number | undefined = undefined;
    lockReconnect = false; //重新连接锁,连接过程中不能再建立重连
    heartBeatTimeOut: number | undefined = undefined;
    messageTimeOut: number | undefined = undefined;
    constructor(options:OptionsParams) {
        //默认30s一次心跳
        options.heartbeat && !options.timeout && (options.timeout = 28 * 1000);
        this.options = options;
    }
    initWebsocket() {
        //初始化weosocket
      const wsuri = "ws://" + "xxxxx" + ":端口号";
      this.websocket = new WebSocket(wsuri);
      this.websocket.onopen = this.onOpen;
      this.websocket.onmessage = this.onMessage;
      this.websocket.onerror = this.onError;
      this.websocket.onclose = this.onClose;
    }
    onOpen() {
      //连接建立之后执行send方法发送数据
      console.log("连接成功");
      let actions = { test: "12345" };
      this.sendData(JSON.stringify(actions));
    }
    onError() {
      //连接建立失败重连
      console.log("连接失败");
    }
    
    onMessage(e:any) {
     //数据接收
      const redata = JSON.parse(e.data);
      console.log(redata);
      this.resetHeartBeat();
    }
    onClose() {
        //关闭
      console.log("断开连接");
    }
    sendData(data: any) {
    //数据发送
      this.websocket && this.websocket.send(data);
    }
    reconnect() {
        let self = this;
        if (self.lockReconnect) {
            return;
        }
        self.lockReconnect = true;
        //没连接上会一直重连,设置延迟避免请求过多
        self.reconnectTimeOut && clearTimeout(self.reconnectTimeOut);
        self.reconnectTimeOut = window.setTimeout(function() {
            //建立新连接
            self.initWebsocket();
            self.lockReconnect = false;
        },4000)
    }
    resetHeartBeat() {
        //开启心跳
      var self = this;
      self.heartBeatTimeOut && clearTimeout(self.heartBeatTimeOut);
      self.heartBeatTimeOut && clearTimeout(self.heartBeatTimeOut);
  
      self.heartBeatTimeOut = window.setTimeout(function() {
        //发送一个心跳
        if (self.websocket && self.websocket.readyState == 1) {
          //如果连接正常
          self.websocket.send("heartCheck");
        } else {
          //否则重连
          self.reconnect();
        }
         self.messageTimeOut = window.setTimeout(function() {
          // 在三秒一次的心跳检测中如果某个值3秒没响应就关掉这次连接
          //超时关闭
          self.websocket && self.websocket.close();
           //建立新连接
          self.initWebsocket();
        }, self.options.timeout);
      }, self.options.timeout);
    }
}