在弱網(wǎng)環(huán)境或者網(wǎng)絡(luò)暫時斷連的情況下悬秉,我們需要一套穩(wěn)定的重連機(jī)制來保證在網(wǎng)絡(luò)不穩(wěn)定的時候吧史,客戶端和服務(wù)端能夠重連鹅巍,繼續(xù)通信剔交。本文將會針對websocket協(xié)議的重連機(jī)制進(jìn)行詳細(xì)描述请敦。
思路
在實(shí)例化websocket后镐躲,我們會定義好各個回調(diào)事件需要執(zhí)行的函數(shù)
var ws = new WebSocket(url);
ws.onclose = function () {
//連接關(guān)閉
};
ws.onerror = function () {
//連接報錯
};
ws.onopen = function () {
//連接成功
};
ws.onmessage = function (event) {
//收到數(shù)據(jù)
}
在弱網(wǎng)環(huán)境下,發(fā)送消息無法抵達(dá)接收端侍筛;抑或萤皂,斷網(wǎng)到瀏覽器約定時限等一些異常情況都會觸發(fā)onclose和onerror,所以理論上,我們只要在onclose和onerror時匣椰,重新創(chuàng)建長連接就可以裆熙。
實(shí)現(xiàn)
根據(jù)上面的簡單思路,代碼如下:
var ws = new WebSocket(url);
ws.onclose = function () {
reconnect()
};
ws.onerror = function () {
reconnect()
};
function reconnect(url) {
setTimeout(function () { //沒連接上會一直重連禽笑,設(shè)置延遲避免請求過多
var ws = new WebSocket(url);
ws.onclose = function () {
reconnect()
};
ws.onerror = function () {
reconnect()
};
}, 2000);
}
稍微抽取再豐富下入录,createWebSocket來創(chuàng)建websocket實(shí)例,initEventHandle來綁定各回調(diào)函數(shù):
var ws = new WebSocket(url);
ws.onclose = function () {
reconnect()
};
ws.onerror = function () {
reconnect()
};
// 重連
function reconnect(url) {
setTimeout(function () { //沒連接上會一直重連佳镜,設(shè)置延遲避免請求過多
createWebSocket(url);
}, 2000);
}
// 實(shí)例websocket
function createWebSocket(url) {
try {
if ('WebSocket' in window) {
ws = new WebSocket(url);
} else if ('MozWebSocket' in window) {
ws = new MozWebSocket(url);
} else {
_alert("當(dāng)前瀏覽器不支持websocket協(xié)議,建議使用現(xiàn)代瀏覽器",3000)
}
initEventHandle();
} catch (e) {
reconnect(url);
}
}
// 初始化事件函數(shù)
function initEventHandle() {
ws.onclose = function () {
reconnect(wsUrl);
};
ws.onerror = function (err) {
reconnect(wsUrl);
};
}
優(yōu)化
弱網(wǎng)僚稿、斷連所導(dǎo)致重連都是被動的,而在一般的websocket連接中都是存在心跳機(jī)制的蟀伸,客戶端和服務(wù)端約定一個特定的心跳消息用于檢測鏈路是否通信正常贫奠。
我們通過心跳機(jī)制,在客戶端來檢測鏈路的正常望蜡,在約定時間間隔內(nèi)收不到心跳或者其他任何通信消息時,客戶端進(jìn)行主動重連拷恨。
所以下面優(yōu)化的脖律,我們需要加一個心跳檢測的方法:
var heartCheck = {
timeout: heartBeatTime*1000, // 心跳檢測時長
timeoutObj: null, // 定時變量
reset: function () { // 重置定時
clearTimeout(this.timeoutObj);
return this;
},
start: function () { // 開啟定時
var self = this;
this.timeoutObj = setTimeout(function () {
// 心跳時間內(nèi)收不到消息,主動觸發(fā)連接關(guān)閉腕侄,開始重連
ws.close();
},this.timeout)
}
}
心跳檢測對象小泉,定義了
- timeout:心跳超時時間
- timeoutObj:定時器變量
- reset:重置心跳
- start:開啟心跳
當(dāng)連接成功時,開啟心跳冕杠;在收到消息時微姊,重置心跳并開啟下一輪檢測,所以我們只需要在onopen和onmessage中加入心跳檢測就行
// 初始化事件函數(shù)
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(); //拿到任何消息都說明當(dāng)前連接是正常的
handleMsg(msg)
};
}
同時觸發(fā)多次重連的問題
在實(shí)際使用過程中兢交,發(fā)現(xiàn)有些瀏覽器,reconnect重連會多次觸發(fā)笼痹,所以需要給重連加一把鎖配喳,當(dāng)一個重連正在執(zhí)行的時候酪穿,無法再觸發(fā)一次重連
function reconnect(url) {
if (reconnect.lockReconnect) return;
reconnect.lockReconnect = true;
setTimeout(function () { //沒連接上會一直重連,設(shè)置延遲避免請求過多
createWebSocket(url);
reconnect.lockReconnect = false;
}, 2000);
}
如果是同個瀏覽器中多個頁面共用一個連接來進(jìn)行通信晴裹,那么就需要使用瀏覽器緩存/數(shù)據(jù)路(localStorage/indexedDB)去加這把鎖被济。