在弱网环境或者网络暂时断连的情况下,我们需要一套稳定的重连机制来保证在网络不稳定的时候,客户端和服务端能够重连,继续通信。本文将会针对websocket协议的重连机制进行详细描述。
思路
在实例化websocket后,我们会定义好各个回调事件需要执行的函数
var ws = new WebSocket(url);
ws.onclose = function () {
//连接关闭
};
ws.onerror = function () {
//连接报错
};
ws.onopen = function () {
//连接成功
};
ws.onmessage = function (event) {
//收到数据
}
在弱网环境下,发送消息无法抵达接收端;抑或,断网到浏览器约定时限等一些异常情况都会触发onclose和onerror,所以理论上,我们只要在onclose和onerror时,重新创建长连接就可以。
实现
根据上面的简单思路,代码如下:
var ws = new WebSocket(url);
ws.onclose = function () {
reconnect()
};
ws.onerror = function () {
reconnect()
};
function reconnect(url) {
setTimeout(function () { //没连接上会一直重连,设置延迟避免请求过多
var ws = new WebSocket(url);
ws.onclose = function () {
reconnect()
};
ws.onerror = function () {
reconnect()
};
}, 2000);
}
稍微抽取再丰富下,createWebSocket来创建websocket实例,initEventHandle来绑定各回调函数:
var ws = new WebSocket(url);
ws.onclose = function () {
reconnect()
};
ws.onerror = function () {
reconnect()
};
// 重连
function reconnect(url) {
setTimeout(function () { //没连接上会一直重连,设置延迟避免请求过多
createWebSocket(url);
}, 2000);
}
// 实例websocket
function createWebSocket(url) {
try {
if ('WebSocket' in window) {
ws = new WebSocket(url);
} else if ('MozWebSocket' in window) {
ws = new MozWebSocket(url);
} else {
_alert("当前浏览器不支持websocket协议,建议使用现代浏览器",3000)
}
initEventHandle();
} catch (e) {
reconnect(url);
}
}
// 初始化事件函数
function initEventHandle() {
ws.onclose = function () {
reconnect(wsUrl);
};
ws.onerror = function (err) {
reconnect(wsUrl);
};
}
优化
弱网、断连所导致重连都是被动的,而在一般的websocket连接中都是存在心跳机制的,客户端和服务端约定一个特定的心跳消息用于检测链路是否通信正常。
我们通过心跳机制,在客户端来检测链路的正常,在约定时间间隔内收不到心跳或者其他任何通信消息时,客户端进行主动重连。
所以下面优化的,我们需要加一个心跳检测的方法:
var heartCheck = {
timeout: heartBeatTime*1000, // 心跳检测时长
timeoutObj: null, // 定时变量
reset: function () { // 重置定时
clearTimeout(this.timeoutObj);
return this;
},
start: function () { // 开启定时
var self = this;
this.timeoutObj = setTimeout(function () {
// 心跳时间内收不到消息,主动触发连接关闭,开始重连
ws.close();
},this.timeout)
}
}
心跳检测对象,定义了
- timeout:心跳超时时间
- timeoutObj:定时器变量
- reset:重置心跳
- start:开启心跳
当连接成功时,开启心跳;在收到消息时,重置心跳并开启下一轮检测,所以我们只需要在onopen和onmessage中加入心跳检测就行
// 初始化事件函数
function initEventHandle() {
ws.onclose = function () {
reconnect(wsUrl);
};
ws.onerror = function (err) {
reconnect(wsUrl);
};
ws.onopen = function () {
heartCheck.reset().start(); //心跳检测重置
};
ws.onmessage = function (msg) { //如果获取到消息,心跳检测重置
heartCheck.reset().start(); //拿到任何消息都说明当前连接是正常的
handleMsg(msg)
};
}
同时触发多次重连的问题
在实际使用过程中,发现有些浏览器,reconnect重连会多次触发,所以需要给重连加一把锁,当一个重连正在执行的时候,无法再触发一次重连
function reconnect(url) {
if (reconnect.lockReconnect) return;
reconnect.lockReconnect = true;
setTimeout(function () { //没连接上会一直重连,设置延迟避免请求过多
createWebSocket(url);
reconnect.lockReconnect = false;
}, 2000);
}
如果是同个浏览器中多个页面共用一个连接来进行通信,那么就需要使用浏览器缓存/数据路(localStorage/indexedDB)去加这把锁。