创建快速稳定的WebSocket长链接
在一个完善的即时信息应用中,WebSocket是极其关键的一环,它为基于Web的即时通讯应用提供了一种全双工的通信机制。但为了提升实际应用场景下的消息即时性和可靠性,我们需要克服WebSocket及其底层依赖的TCP连接对于复杂网络情况下的不稳定性,即时通讯的开发者们通常都需要为其设计一套完整的连接保活、验活以及断片网重连方案。就断网重连而言,其重连响应速度将严重影响了上层应用的“即时性”和用户体验。
因此,如何在复杂网络场景下,更即时快速地感知网络变动,并快速恢复WebSocket的可用性,就变得尤为重要。本文分享WebSocket在不同状态下、不同的网络状态下,应该如何实现快速断网重连。
1. WebSocket长链接的基本使用
// 定义几个变量检测状态
var WS_URL = "ws://127.0.0.1:9225/ws"; // websocket地址 http对应ws https对应wss
var timeout = 30*1000; // 心跳检测时间
var timeoutObj = null; // 心跳心跳倒计时
var serverTimeoutObj = null; // 心跳倒计时
var lockReconnect = false; // 防止
var timeoutnum = null; // 断开重连倒计时
// 创建websocket
function initWebSocket(){
// WebSocket与普通的请求所用协议有所不同,ws等同于http,wss等同于https
window.websocket = new WebSocket(WS_URL);
// websocket链接成功回调
window.websocket.onopen = websocketonopen;
// websocket链接发生错误回调
window.websocket.onerror = websocketonerror;
// websocket链接获取到消息调用注册全局websocket消息接收事件
window.websocket.onmessage = websocketonmessage;
// websocket断开消息回调
window.websocket.onclose = websocketonclose;
}
// onopen回调函数
function websocketonopen() {
// 连接成功后开启心跳
heart();
console.log("WebSocket连接成功!!!" + new Date() + "----" + window.websocket.readyState);
}
// onerror回调函数
function websocketonerror(e) {
// 如果发生链接错误 则关闭websocket
closeWebSocket();
console.log("WebSocket连接发生错误,重启socket");
console.log('Error:',e);
}
// onclose回调函数
function websocketonclose(e) {
window.websocket.close();
clearTimeout(timeoutObj);
clearTimeout(serverTimeoutObj);
console.log("WebSocket连接关闭,重启socket");
// 链接关闭,执行重新连接
reconnect();
}
// onmessage回调函数
function websocketonmessage(event) {
// 重置心跳检测
reset();
// 自定义全局监听事件
window.dispatchEvent(
new CustomEvent("onWebSocketMessage", {
detail: {
data: event.data,
},
})
);
}
// 关闭WebSocket
function closeWebSocket() {
// 关闭websocket
window.websocket.close();
}
2. WebSocket使用心跳检测和断开重新连接
为什么使用心跳检测?
因为WebSocket底层基于tcp连接,所以在有些时候是无法触发onclose函数来感知WebSocket链接已经断开的,例如:切换网络、链路中间路由崩溃、链路的前端出口不可用、服务器负载持续过高无法响应;导致链接断开的原因有很多难以把控,所以通过心跳检测的方式定时向服务端发送消息,服务端返回心跳检测消息,前端根据消息判断是否出现err,如果出现err则说明连接断开,进行重新连接,通过这样可以保证链接的;
// 心跳检测函数
function heart(){
//清除延时器
timeoutObj && clearTimeout(timeoutObj);
serverTimeoutObj && clearTimeout(serverTimeoutObj);
timeoutObj = setTimeout(() => {
if (window.websocket && window.websocket.readyState == 1) {
//发送消息,服务端返回信息,即表示连接良好,可以在socket的onmessage事件重置心跳机制函数
window.websocket.send("heart");
} else {
//如果不返回消息 则执行WebSocket重连
reconnect();
}
//定义一个延时器等待服务器响应,若超时,则关闭连接,重新请求server建立socket连接
serverTimeoutObj = setTimeout(() => {
window.websocket.close();
}, timeout);
}, timeout);
}
// 收到消息后重新执行心跳检测
function reset() {
// 重置心跳 清除时间
clearTimeout(timeoutObj);
clearTimeout(serverTimeoutObj);
// 重启心跳
heart();
}
// 重连接函数
function reconnect(){
if (lockReconnect) return;
lockReconnect = true;
//没连接上会一直重连,设置延迟避免请求过多
timeoutnum && clearTimeout(timeoutnum);
timeoutnum = setTimeout(() => {
initWebSocket();
lockReconnect = false;
}, 5000);
}
3. 监测电脑网络状态,实现断开快速重新连接
javascript通过监听online和offline事件判断网络状态
心跳检测的弊端在于定时发送请求,即时性不够。通过网络状态监测可以更快感知断开,弥补心跳检测的缺点,使链接更快速,系统更稳定
// 监听网络状态
window.addEventListener("online", () => {
console.log("网络正常");
});
window.addEventListener("offline", () => {
console.log("网络断开");
// 网络断开则重连
reconnect()
});
4. 注册全局WebSocket监听事件,保证在模块化开发中任何地方都能用到同一个WebSocket,不用重新创建连接。
// onmessage回调函数
function websocketonmessage(event) {
// 重置心跳检测
reset();
// 自定义全局监听事件
window.dispatchEvent(
new CustomEvent("onWebSocketMessage", {
detail: {
data: event.data,
},
})
);
}
// 全局可以监听websocket onmessage事件
window.addEventListener("onWebSocketMessage", businessFunction);
// 获取到websocket返回执行业务代码
function businessFunction(data){
console.log("Data::::",data)
}