fingerboy
文章10
标签4
分类3
创建快速稳定的WebSocket长链接

创建快速稳定的WebSocket长链接

创建快速稳定的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)
}
本文作者:fingerboy
本文链接:http://blog.icandy.top/2024/05/21/00003/