理解WebSocket心跳及重连机制

在使用websocket的过程中,有时候会遇到网络断开的情况,但是在网络断开的时候服务器端并没有触发onclose的事件。这样会有:服务器会继续向客户端发送多余的链接,并且这些数据还会丢失。所以就需要一种机制来检测客户端和服务端是否处于正常的链接状态。因此就有了websocket的心跳了。还有心跳,说明还活着,没有心跳说明已经挂掉了。

1. 为什么叫心跳包呢?
它就像心跳一样每隔固定的时间发一次,来告诉服务器,我还活着。

2. 心跳机制是?
心跳机制是每隔一段时间会向服务器发送一个数据包,告诉服务器自己还活着,同时客户端会确认服务器端是否还活着,如果还活着的话,就会回传一个数据包给客户端来确定服务器端也还活着,否则的话,有可能是网络断开连接了。需要重连~

那么需要怎么去实现它呢?如下所有代码:

<html>
<head><meta charset="utf-8"><title>WebSocket heartcheck</title>
</head>
<body><script type="text/javascript">var lockReconnect = false;//避免重复连接var wsUrl = "ws://localhost:8080/heartcheck";var ws;var tt;function createWebSocket() {try {ws = new WebSocket(wsUrl);init();} catch(e) {console.log('catch');reconnect(wsUrl);}}function init() {ws.onclose = function () {console.log('链接关闭');reconnect(wsUrl);};ws.onerror = function() {console.log('发生异常了');reconnect(wsUrl);};ws.onopen = function () {//心跳检测重置heartCheck.start();};ws.onmessage = function (event) {//拿到任何消息都说明当前连接是正常的console.log('接收到消息');heartCheck.start();}}function reconnect(url) {if(lockReconnect) {return;};lockReconnect = true;//没连接上会一直重连,设置延迟避免请求过多tt && clearTimeout(tt);tt = setTimeout(function () {createWebSocket(url);lockReconnect = false;}, 4000);}//心跳检测var heartCheck = {timeout: 3000,timeoutObj: null,serverTimeoutObj: null,start: function(){console.log('start');var self = this;this.timeoutObj && clearTimeout(this.timeoutObj);this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj);this.timeoutObj = setTimeout(function(){//这里发送一个心跳,后端收到后,返回一个心跳消息,ws.send("Client HeartBeat Test Message.");self.serverTimeoutObj = setTimeout(function() {console.log(111);console.log(ws);ws.close();// createWebSocket();}, self.timeout);}, this.timeout)}}createWebSocket(wsUrl);</script>
</body>
</html>

具体的思路如下:
1. 第一步页面初始化,先调用createWebSocket函数,目的是创建一个websocket的方法:new WebSocket(wsUrl);因此封装成函数内如下代码:

function createWebSocket() {try {ws = new WebSocket(wsUrl);init();} catch(e) {console.log('catch');reconnect(wsUrl);}
}

2. 第二步调用init方法,该方法内把一些监听事件封装如下:

function init() {ws.onclose = function () {console.log('链接关闭');reconnect(wsUrl);};ws.onerror = function() {console.log('发生异常了');reconnect(wsUrl);};ws.onopen = function () {//心跳检测重置heartCheck.start();};ws.onmessage = function (event) {//拿到任何消息都说明当前连接是正常的console.log('接收到消息');heartCheck.start();}
}

3. 如上第二步,当网络断开的时候,会先调用onerror,onclose事件可以监听到,会调用reconnect方法进行重连操作。正常的情况下,是先调用
onopen方法的,当接收到数据时,会被onmessage事件监听到。

4. 重连操作 reconnect代码如下:

var lockReconnect = false;//避免重复连接
function reconnect(url) {if(lockReconnect) {return;};lockReconnect = true;//没连接上会一直重连,设置延迟避免请求过多tt && clearTimeout(tt);tt = setTimeout(function () {createWebSocket(url);lockReconnect = false;}, 5000);
}

如上代码,如果网络断开的话,会执行reconnect方法,使用了一个定时器,4秒后会重新创建一个新的websocket链接,重新调用createWebSocket函数,
重新会执行及发送数据给服务器端。

5. 最后一步就是实现心跳检测的代码:如下:

//心跳检测
var heartCheck = {timeout: 30000,timeoutObj: null,serverTimeoutObj: null,start: function(){console.log('start');var self = this;this.timeoutObj && clearTimeout(this.timeoutObj);this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj);this.timeoutObj = setTimeout(function(){//这里发送一个心跳,后端收到后,返回一个心跳消息,//onmessage拿到返回的心跳就说明连接正常ws.send("Client HeartBeat Test Message.");self.serverTimeoutObj = setTimeout(function() {//如果onclose会执行reconnect,我们执行websocket.close()就行了.如果直接执行reconnect 会触发onclose导致重连两次ws.close();}, self.timeout);}, this.timeout)}
}

实现心跳检测的思路是:每隔一段固定的时间,向服务器端发送一个ping数据,如果在正常的情况下,服务器会返回一个pong给客户端,如果客户端通过
onmessage事件能监听到的话,说明请求正常,这里我们使用了一个定时器,每隔3秒的情况下,如果是网络断开的情况下,在指定的时间内服务器端并没有返回心跳响应消息,因此服务器端断开了,因此这个时候我们使用ws.close关闭连接,在一段时间后(在不同的浏览器下,时间是不一样的,firefox响应更快),
可以通过 onclose事件监听到。因此在onclose事件内,我们可以调用 reconnect事件进行重连操作。

加强版的reconnectingwebsocket.js实现:


(function (global, factory) {if (typeof define === 'function' && define.amd) {define([], factory);} else if (typeof module !== 'undefined' && module.exports){module.exports = factory();} else {global.ReconnectingWebSocket = factory();}
})(this, function () {if (!('WebSocket' in window)) {return;}function ReconnectingWebSocket(url, protocols, options) {// Default settingsvar settings = {/** Whether this instance should log debug messages. */debug: false,/** Whether or not the websocket should attempt to connect immediately upon instantiation. */automaticOpen: true,/** The number of milliseconds to delay before attempting to reconnect. */reconnectInterval: 1000,/** The maximum number of milliseconds to delay a reconnection attempt. */maxReconnectInterval: 30000,/** The rate of increase of the reconnect delay. Allows reconnect attempts to back off when problems persist. */reconnectDecay: 1.5,/** The maximum time in milliseconds to wait for a connection to succeed before closing and retrying. */timeoutInterval: 2000,/** The maximum number of reconnection attempts to make. Unlimited if null. */maxReconnectAttempts: null,/** The binary type, possible values 'blob' or 'arraybuffer', default 'blob'. */binaryType: 'blob'}if (!options) { options = {}; }// Overwrite and define settings with options if they exist.for (var key in settings) {if (typeof options[key] !== 'undefined') {this[key] = options[key];} else {this[key] = settings[key];}}// These should be treated as read-only properties/** The URL as resolved by the constructor. This is always an absolute URL. Read only. */this.url = url;/** The number of attempted reconnects since starting, or the last successful connection. Read only. */this.reconnectAttempts = 0;/*** The current state of the connection.* Can be one of: WebSocket.CONNECTING, WebSocket.OPEN, WebSocket.CLOSING, WebSocket.CLOSED* Read only.*/this.readyState = WebSocket.CONNECTING;/*** A string indicating the name of the sub-protocol the server selected; this will be one of* the strings specified in the protocols parameter when creating the WebSocket object.* Read only.*/this.protocol = null;// Private state variablesvar self = this;var ws;var forcedClose = false;var timedOut = false;var eventTarget = document.createElement('div');// Wire up "on*" properties as event handlerseventTarget.addEventListener('open',       function(event) { self.onopen(event); });eventTarget.addEventListener('close',      function(event) { self.onclose(event); });eventTarget.addEventListener('connecting', function(event) { self.onconnecting(event); });eventTarget.addEventListener('message',    function(event) { self.onmessage(event); });eventTarget.addEventListener('error',      function(event) { self.onerror(event); });// Expose the API required by EventTargetthis.addEventListener = eventTarget.addEventListener.bind(eventTarget);this.removeEventListener = eventTarget.removeEventListener.bind(eventTarget);this.dispatchEvent = eventTarget.dispatchEvent.bind(eventTarget);/*** This function generates an event that is compatible with standard* compliant browsers and IE9 - IE11** This will prevent the error:* Object doesn't support this action** http://stackoverflow.com/questions/19345392/why-arent-my-parameters-getting-passed-through-to-a-dispatched-event/19345563#19345563* @param s String The name that the event should use* @param args Object an optional object that the event will use*/function generateEvent(s, args) {var evt = document.createEvent("CustomEvent");evt.initCustomEvent(s, false, false, args);return evt;};this.open = function (reconnectAttempt) {ws = new WebSocket(self.url, protocols || []);ws.binaryType = this.binaryType;if (reconnectAttempt) {if (this.maxReconnectAttempts && this.reconnectAttempts > this.maxReconnectAttempts) {return;}} else {eventTarget.dispatchEvent(generateEvent('connecting'));this.reconnectAttempts = 0;}if (self.debug || ReconnectingWebSocket.debugAll) {console.debug('ReconnectingWebSocket', 'attempt-connect', self.url);}var localWs = ws;var timeout = setTimeout(function() {if (self.debug || ReconnectingWebSocket.debugAll) {console.debug('ReconnectingWebSocket', 'connection-timeout', self.url);}timedOut = true;localWs.close();timedOut = false;}, self.timeoutInterval);ws.onopen = function(event) {clearTimeout(timeout);if (self.debug || ReconnectingWebSocket.debugAll) {console.debug('ReconnectingWebSocket', 'onopen', self.url);}self.protocol = ws.protocol;self.readyState = WebSocket.OPEN;self.reconnectAttempts = 0;var e = generateEvent('open');e.isReconnect = reconnectAttempt;reconnectAttempt = false;eventTarget.dispatchEvent(e);};ws.onclose = function(event) {clearTimeout(timeout);ws = null;if (forcedClose) {self.readyState = WebSocket.CLOSED;eventTarget.dispatchEvent(generateEvent('close'));} else {self.readyState = WebSocket.CONNECTING;var e = generateEvent('connecting');e.code = event.code;e.reason = event.reason;e.wasClean = event.wasClean;eventTarget.dispatchEvent(e);if (!reconnectAttempt && !timedOut) {if (self.debug || ReconnectingWebSocket.debugAll) {console.debug('ReconnectingWebSocket', 'onclose', self.url);}eventTarget.dispatchEvent(generateEvent('close'));}var timeout = self.reconnectInterval * Math.pow(self.reconnectDecay, self.reconnectAttempts);setTimeout(function() {self.reconnectAttempts++;self.open(true);}, timeout > self.maxReconnectInterval ? self.maxReconnectInterval : timeout);}};ws.onmessage = function(event) {if (self.debug || ReconnectingWebSocket.debugAll) {console.debug('ReconnectingWebSocket', 'onmessage', self.url, event.data);}var e = generateEvent('message');e.data = event.data;eventTarget.dispatchEvent(e);};ws.onerror = function(event) {if (self.debug || ReconnectingWebSocket.debugAll) {console.debug('ReconnectingWebSocket', 'onerror', self.url, event);}eventTarget.dispatchEvent(generateEvent('error'));};}// Whether or not to create a websocket upon instantiationif (this.automaticOpen == true) {this.open(false);}/*** Transmits data to the server over the WebSocket connection.** @param data a text string, ArrayBuffer or Blob to send to the server.*/this.send = function(data) {if (ws) {if (self.debug || ReconnectingWebSocket.debugAll) {console.debug('ReconnectingWebSocket', 'send', self.url, data);}return ws.send(data);} else {throw 'INVALID_STATE_ERR : Pausing to reconnect websocket';}};/*** Closes the WebSocket connection or connection attempt, if any.* If the connection is already CLOSED, this method does nothing.*/this.close = function(code, reason) {// Default CLOSE_NORMAL codeif (typeof code == 'undefined') {code = 1000;}forcedClose = true;if (ws) {ws.close(code, reason);}};/*** Additional public API method to refresh the connection if still open (close, re-open).* For example, if the app suspects bad data / missed heart beats, it can try to refresh.*/this.refresh = function() {if (ws) {ws.close();}};}/*** An event listener to be called when the WebSocket connection's readyState changes to OPEN;* this indicates that the connection is ready to send and receive data.*/ReconnectingWebSocket.prototype.onopen = function(event) {};/** An event listener to be called when the WebSocket connection's readyState changes to CLOSED. */ReconnectingWebSocket.prototype.onclose = function(event) {};/** An event listener to be called when a connection begins being attempted. */ReconnectingWebSocket.prototype.onconnecting = function(event) {};/** An event listener to be called when a message is received from the server. */ReconnectingWebSocket.prototype.onmessage = function(event) {};/** An event listener to be called when an error occurs. */ReconnectingWebSocket.prototype.onerror = function(event) {};/*** Whether all instances of ReconnectingWebSocket should log debug messages.* Setting this to true is the equivalent of setting all instances of ReconnectingWebSocket.debug to true.*/ReconnectingWebSocket.debugAll = false;ReconnectingWebSocket.CONNECTING = WebSocket.CONNECTING;ReconnectingWebSocket.OPEN = WebSocket.OPEN;ReconnectingWebSocket.CLOSING = WebSocket.CLOSING;ReconnectingWebSocket.CLOSED = WebSocket.CLOSED;return ReconnectingWebSocket;
});

理解WebSocket心跳及重连机制以及加强版的reconnectingwebsocket.js实现相关推荐

  1. php websocket 心跳包,websocket 心跳包重连

    上次我们讲过了websocket断线重连的问题,那么久会有人提出疑问了,心跳包重连跟断线重连有什么区别呢? 其实这两个都是为了达到一个目的,那就是保证当前设备的网络状态保持通畅...而断线重连呢,只能 ...

  2. paho架构_MQTT系列最终章-Paho源码分析(三)-心跳与重连机制

    写在前面 通过之前MQTT系列-Eclipse.Paho源码分析(二)-消息的发送与接收的介绍,相信仔细阅读过的小伙伴已经对Eclipse.Paho内部发送和订阅消息的流程有了一个较为清晰的认识,今天 ...

  3. websocket心跳链接代码_WebSocket原理与实践(五)--心跳及重连机制

    在使用websocket的过程中,有时候会遇到网络断开的情况,但是在网络断开的时候服务器端并没有触发onclose的事件.这样会有:服务器会继续向客户端发送多余的链接,并且这些数据还会丢失.所以就需要 ...

  4. WebSocket的心跳重连机制

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

  5. WebSocket心跳

    理解WebSocket心跳及重连机制(五) - 百度文库 情况:当客户端网络断开无法接收信息,服务端依旧持续向客户端发送信息,造成数据丢失. 所以就需要一种机制检测客户端和服务端是否处于正常链接的状态 ...

  6. Websocket心跳检测、重连机制

    前言 为了获取实时数据,前端需要和后端保持通信,HTTP 协议只能是客户端向服务器发出请求,服务器返回查询结果.这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦.我们只能使 ...

  7. java 心跳 断网重连_工作笔记5 - websocket心跳重连机制

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

  8. websocket中的心跳重连机制

    websocket中的心跳重连机制目的:检测客户端和服务端是否处于正常连接的状态. 思路: 每隔一段指定的时间(定时器),向服务器发送一个数据,服务器收到数据后再发送给客户端,正常情况下客户端通过on ...

  9. WebSocket心跳重连机制

    阅读本文章前请先了解WebSocket 场景 WebSocket在连接关闭的情况下会触发onclose事件,在链接异常的情况下会触发onerror事件.而在弱网条件下,onclose事件触发的灵敏度却 ...

  10. 【websocket前后端交互】vue-springboot实现websocket前后端交互链接,websocket心跳重连,包含前后端代码,复制即可用【详细解释版本】

    前言: 还是老规矩,一步步的教大家如何建立前后端的 websocket 链接,并能完成互相传送数据的简单功能.由于网上找了半天发现很多帖子都是东一句西一句的,要不就是写的没什么注释和解释,导致我这个前 ...

最新文章

  1. mysql shharding_mysql 技术内幕 的示例数据库
  2. 批处理中的****1nul 2nul
  3. SAP CRM WebClient UI里的文件是如何上传到ABAP Netweaver后台的
  4. 【LeetCode笔记】64. 最小路径和(Java、动态规划)
  5. Python、TensorFlow、机器学习、深度学习四件套(附免费下载)
  6. web项目目录访问 路径
  7. C语言面向对象(下):驱动设计技巧
  8. 测试实践:华为测试流程总结
  9. IOS开发Swift笔记19-扩展(Extension)
  10. Windows使用ROS机器人操作系统12记录
  11. - 模块“VPMC“启动失败,未能启动虚拟机?
  12. python average函数详解_Python内置函数详解——总结篇
  13. 2.7.0 gitk 打不开 Error in startup script: unknown color name lime
  14. 【TigerGraph】图在复杂制造业中的应用
  15. 分享 10 个最常见的 JavaScript 问题
  16. 文明重启战局服务器维护中,王牌战争文明重启8月23日更新公告
  17. mysql 查看slave状态_解读show slave status 命令判断MySQL复制同步状态
  18. 互联网舆情系统功能调研和详细点调研
  19. C++是TIOBE 2022年度最佳编程语言!
  20. 发那科系统整套PMC梯形图设计调试维修 FANUC全套PMC设计

热门文章

  1. 12. JavaScript Number 对象
  2. php指定时间 n天,PHP实现指定时间的n月之前的这一天的两种算法
  3. MySQL字符集 GBK、GB2312、UTF8区别 解决 MYSQL中文乱码问题
  4. 编程语言 - PHP
  5. vue-cli webpack浅析
  6. 常见前端面试题及答案(下)
  7. Spring AOP前置通知和后置通知
  8. [转] TextField的restrict属性妙用
  9. Visual Studio 常用宏
  10. raid卡 4k 设置 linux,硬盘“大户”看过来,手把手教你组建 RAID 磁盘阵列