提示:文章最下方有仓库地址

心跳重连缘由

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

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

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

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

如何实现

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

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上绑定重新连接方法。

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

这样一般正常情况下失去连接时,触发onclose方法,我们就能执行重连了。

那么针对断网情况的心跳重连,怎么实现呢,我们只需要定时的发送消息,去触发websocket.send方法,如果网络断开了,浏览器便会触发onclose。

简单的实现:

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秒之后,执行心跳检测,看是不是断连了,这个检测时间可以自己根据自身情况设定。

判断前端websocket断开(断网但不限于断网的情况):

当心跳检测执行send方法之后,如果当前websocket是断开状态(或者说断网了),发送超时之后,浏览器的websocket会自动触发onclose方法,重连就会立刻执行(onclose方法体绑定了重连事件),如果当前一直是断网状态,重连会2秒(时间是自己代码设置的)执行一次直到网络正常后连接成功。

如此一来,判断前端断开websocket的心跳检测就实现了。为什么说是前端主动断开,因为当前这种情况主要是通过前端websocket.send来检测并触发的onclose,后面说后端断开的情况。

我本想测试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本身就有底层的心跳,socket消息发送不出去的时候,会等待一定时间看是否能在这个时间之内再次连接上,如果超时便会触发onclose。

判断后端断开:

如果后端因为一些情况断开了ws,是可控情况下的话,会下发一个断连的通知,这样会触发前端weboscket的onclose方法,我们便会重连。

如果因为一些异常断开了连接,前端是不会感应到的,所以如果前端发送了心跳一定时间之后,后端既没有返回心跳响应消息,前端也没有收到任何其他消息的话,我们就能断定后端发生异常断开了。

一点特别重要的发送心跳到后端,后端收到消息之后必须返回消息,否则超过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,否则会出现一只相互挤下线的死循环。

结语

由于断开等原因可能会导致发送的数据没有发送出去,要保证数据不丢失的话,可以做消息回执,也就是a给b发送消息id=1,b返回收到id=1的消息,如果没有回执a可以再次发送消息id=1。

由上文可以看到,我们使用了前端发送ping,后端返回pong的这样一种心跳的方式。也有一种方式是后端主动发送心跳,前端判断是否超时。因为ws链接必须是前端主动请求建立连接,因此重连肯定是给前端来做,所以判断重连逻辑都是写在前端。

上面所说第二种方式是让服务端发送心跳,前端来接收,这样的方式会多节约一点带宽,因为如果是前端发送心跳,后端需要返回心跳,也就是ping pong的过程会有两次数据传递。  而后端来发送心跳的话,就只需要发送ping,前端不需要回应。但是这样造成了一个问题。前端需要和后端约定好心跳间隔,比如后端设置10秒发送一次心跳,那前端就需要设置一个安全值,比如距离上次收到心跳超过12秒还没收到下一个心跳就重连。这种方式的问题在于调节时间就变得不那么灵活了,需要双方都同时确定一个时间约定。后端的逻辑也会比较多一点。
而如果前端来发送ping 后端返回pong的话,那么间隔时间就只需要前端自己控制了。加上我的代码把收到的任何后端信息都可以当作是连接正常,从而重置心跳时间,这样也节约了一些请求次数。
使用我这样的方式,后端比较轻松,只需要在 onmessage 写一段代码,大概如下:

if(msg=='heartbeat') socket.send(anything);

封装了一个npm包,欢迎使用

https://github.com/zimv/websocket-heartbeat-js

https://www.npmjs.com/package/websocket-heartbeat-js

初探和实现websocket心跳重连的更多相关文章

  1. 初探和实现websocket心跳重连(npm: websocket-heartbeat-js)

    提示:文章最下方有仓库地址 心跳重连缘由 websocket是前后端交互的长连接,前后端也都可能因为一些情况导致连接失效并且相互之间没有反馈提醒.因此为了保证连接的可持续性和稳定性,websocket ...

  2. websocket心跳重连 websocket-heartbeat-js

    初探和实现websocket心跳重连(npm: websocket-heartbeat-js) 心跳重连缘由 websocket是前后端交互的长连接,前后端也都可能因为一些情况导致连接失效并且相互之间 ...

  3. websocket 心跳重连

    websocket 的基本使用: var ws = new WebSocket(url); ws.onclose = function () { //something reconnect(); // ...

  4. 161114、websocket实现心跳重连

    心跳重连缘由 在使用websocket过程中,可能会出现网络断开的情况,比如信号不好,或者网络临时性关闭,这时候websocket的连接已经断开, 而浏览器不会执行websocket 的 onclos ...

  5. WebSocket心跳检测和重连机制

    1. 心跳重连原由 心跳和重连的目的用一句话概括就是客户端和服务端保证彼此还活着,避免丢包发生. websocket连接断开有以下两证情况: 前端断开 在使用websocket过程中,可能会出现网络断 ...

  6. 理解WebSocket心跳及重连机制(五)

    理解WebSocket心跳及重连机制 在使用websocket的过程中,有时候会遇到网络断开的情况,但是在网络断开的时候服务器端并没有触发onclose的事件.这样会有:服务器会继续向客户端发送多余的 ...

  7. 【简记】前端对接WebSocket与心跳重连

    前言 最近又在忙着开发别的模块,其中包含了即时通讯这一块,上一次做即时通讯时还是去年年底,一时间代码都在自己的笔记本里,还没带--这里就记录一下前端对接WebSocket的实现,包含心跳重连,简记之. ...

  8. 小程序websocket心跳库——websocket-heartbeat-miniprogram

    前言 在16年的时候因为项目接触到websocket,而后对心跳重连做了一次总结,写了篇博客,而后18年对之前github上的demo代码进行了再次开发和开源,最终封装成库.如下: 博客:https: ...

  9. ws & websocket & 掉线重连

    ws & websocket & 掉线重连 reconnecting websocket https://github.com/joewalnes/reconnecting-webso ...

随机推荐

  1. txt文本变成html

    file_name = 'x.txt' f = open(file_name,'r') file_result = 'x.html' str_head = " LINE CI UTIL&qu ...

  2. swiper的初步使用

    1.引入文件,顺序引入(此处基于jquery,且版本至少1.7以上) <link rel="stylesheet" href="path/to/swiper-3.4 ...

  3. 制作dll自动注册工具

    记录一个简单的dll自动注册工具制作:主要用到的是DllRegisterServer()方法,其实我们平常注册dll文件内部都会调用这个方法. 这里我就直接写在主程序里面了,需要注意的地方也直接在代码 ...

  4. WPF整理-二进制资源和内容

    WPF中的Binary Resource(二进制资源)是相对于前面所说的Logical resource(逻辑资源)而说的,一般指Image.XML文件等. 注意:这里说的是Resource" ...

  5. Java面试查漏补缺

    一.基础 1.&和&&的区别. [概述] &&只能用作逻辑与(and)运算符(具有短路功能):但是&可以作为逻辑与运算符(是“无条件与”,即没有短路的功 ...

  6. 初试Office 365企业版E3

    Microsoft Office 365 团队给了所有现任的MVP 12个月的微软 Office 365 企业 E3 订阅,今天激活账号并试用了一下,发现非常强大,本文简要介绍下Office 365 ...

  7. 在Linux上编译dotnet cli的源代码生成.NET Core SDK的安装包

    .NET 的开源,有了更多的DIY乐趣.这篇博文记录一下在新安装的 Linux Ubuntu 14.04 上通过自己动手编译 dotnet cli 的源代码生成 .net core sdk 的 deb ...

  8. [.net 面向对象程序设计深入](5)MVC 6 —— 构建跨平台.NET开发环境(Windows/Mac OS X/Linux)

    [.net 面向对象程序设计深入](5)MVC 6 —— 构建跨平台.NET开发环境(Windows/Mac OS X/Linux) 1.关于跨平台 上篇中介绍了MVC的发展历程,说到ASP.NET ...

  9. ASP.NET MVC 5 Web编程2 -- URL映射(路由原理)

    本章将讲述ASP.NET MVC5 的路由原理,即URL映射机制. 简单点就是解释:为什么MVC在浏览器输入地址就能访问到类(或类中的方法)?这是怎么做到的?我自己可以通过.NET写出一个自己的MVC ...

  10. CSS3 Flexbox不迷路指南

    Flexbox早有耳闻,但是决定切实尝试一番,还是因为看了这条围脖: 我觉得用flexbox可以实现,但是发觉无从下手,属性特性都不了解.趁此机会,学习下. 英文原版参考资料在这里:A Complet ...