初探和实现websocket心跳重连

初探和实现websocket心跳重连

心跳重连原因

在使用websocket过程当中,可能会出现网络断开的状况,好比信号很差,或者网络临时性关闭,这时候websocket的链接已经断开, 而浏览器不会执行websocket 的 onclose方法,咱们没法知道是否断开链接,也就没法进行重连操做。 若是当前发送websocket数据到后端,一旦请求超时,onclose便会执行,这时候即可进行绑定好的重连操做。 所以websocket心跳重连就应运而生。前端

如何实现

在websocket实例化的时候,咱们会绑定一些事件:git

var ws = new WebSocket(url);
ws.onclose = function () {
    //something
};
ws.onerror = function () {
    //something
};
        
ws.onopen = function () {
   //something
};
ws.onmessage = function (event) {
   //something
}

若是但愿websocket链接一直保持,咱们会在close或者error上绑定从新链接方法。github

ws.onclose = function () {
    reconnect();
};
ws.onerror = function () {
    reconnect();
};

这样通常正常状况下失去链接时,触发onclose方法,咱们就能执行重连了。web

那么针对断网的状况的心跳重连,怎么实现呢。 简单的实现:chrome

var heartCheck = {
    timeout: 60000,//60ms
    timeoutObj: null,
    reset: function(){
        clearTimeout(this.timeoutObj);
     this.start();
    },
    start: function(){
        this.timeoutObj = setTimeout(function(){
            ws.send("HeartBeat");
        }, this.timeout)
    }
}

ws.onopen = function () {
   heartCheck.start();
};
ws.onmessage = function (event) {
    heartCheck.reset();
}

如上代码,heartCheck 的 reset和start方法主要用来控制心跳的定时。后端

什么条件下执行心跳:

当onopen也就是链接上时,咱们便开始start计时,若是在定时时间范围内,onmessage获取到了后端的消息,咱们就重置倒计时, 距离上次从后端获取到消息超过60秒以后,执行心跳检测,看是否是断连了,这个检测时间能够本身根据自身状况设定。浏览器

判断前端ws断开(断网但不限于断网的状况):

小心跳检测send方法执行以后,若是当前websocket是断开状态(或者说断网了),发送超时以后,浏览器的ws会自动触发onclose方法,重连也执行了(onclose方法体绑定了重连事件),若是当前一直是断网状态,重连会2秒(时间是本身代码设置的)执行一次直到网络正常后链接成功。 如此一来,咱们判断前端主动断开ws的心跳检测就实现了。为何说是前端主动断开,由于当前这种状况主要是经过前端ws的事件来判断的,后面说后端主动断开的状况。websocket

我本想测试websocket超时时间,又发现了一些新的问题网络

  1. 在chrome中,若是心跳检测 也就是websocket实例执行send以后,15秒内没发送到另外一接收端,onclose便会执行。那么超时时间是15秒。
  2. 我又打开了Firefox ,Firefox在断网7秒以后,直接执行onclose。说明在Firefox中不须要心跳检测便能自动onclose。
  3. 同一代码, reconnect方法 在chrome 执行了一次,Firefox执行了两次。固然咱们在几处地方(代码逻辑处和websocket事件处)绑定了reconnect(), 因此保险起见,咱们仍是给reconnect()方法加上一个锁,保证只执行一次

目前来看不一样的浏览器,有不一样的机制,不管浏览器websocket自身会不会在断网状况下执行onclose,加上心跳重连后,已经能保证onclose的正常触发。socket

判断后端断开:

若是后端由于一些状况断开了ws,是可控状况下的话,会下发一个断连的消息通知,以后才会断开,咱们便会重连。

若是由于一些异常断开了链接,咱们是不会感应到的,因此若是咱们发送了心跳必定时间以后,后端既没有返回心跳响应消息,前端又没有收到任何其余消息的话,咱们就能判定后端主动断开了。 一点特别重要的发送心跳到后端,后端收到消息以后必须返回消息,不然超过60秒以后会断定后端主动断开了。再改造下代码:

var heartCheck = {
    timeout: 60000,//60ms
    timeoutObj: null,
    serverTimeoutObj: null,
    reset: function(){
        clearTimeout(this.timeoutObj);
        clearTimeout(this.serverTimeoutObj);
     this.start();
    },
    start: function(){
        var self = this;
        this.timeoutObj = setTimeout(function(){
            ws.send("HeartBeat");
            self.serverTimeoutObj = setTimeout(function(){
                ws.close();//若是onclose会执行reconnect,咱们执行ws.close()就好了.若是直接执行reconnect 会触发onclose致使重连两次
            }, self.timeout)
        }, this.timeout)
    },
}

ws.onopen = function () {
   heartCheck.start();
};
ws.onmessage = function (event) {
    heartCheck.reset();
}
ws.onclose = function () {
    reconnect();
};
ws.onerror = function () {
    reconnect();
};

PS:

由于目前咱们这种方式会一直重连若是没链接上或者断连的话,若是有两个设备同时登录而且会踢另外一端下线,必定要发送一个踢下线的消息类型,这边接收到这种类型的消息,逻辑判断后就再也不执行reconnect,不然会出现一只相互挤下线的死循环。

整理了一个简单的demo到github,点击查看https://github.com/zimv/WebSocketHeartBeat