关于 uniapp 的 WebSocket 封装(心跳检测、断线重连)

3,340 阅读1分钟

前言

基于 uniapp 提供的 WebSocket Api 来进行封装,让你的 WebSocket 功能更加强大!

tip: 后续考虑做成 uniapp 插件,敬请期待。 🙏

实现思路

心跳检测

客户端定时向服务端发送"ping",如果服务端在一定的时间内,没有返回给客户端"pong",那么就判断为离线。

sequenceDiagram
    participant 客户端
    participant 服务端
    客户端->>服务端: ping
    服务端->>客户端: pong

断线重连

客户端监测到服务端断线后,就重新初始化 WebSocket 类,然后再进行检测,反复操作,直到连接成功为止。

代码实现

websocket.js

function pickJsonObj(value) {
	try {
		return JSON.parse(value)
	} catch (e) {
		return null;
	}
}

class WebSocketClient {
	constructor(url) {
		this.url = url;
		this.socket = null;
		this.isReconnecting = false;
		this.reconnectInterval = 3000; // 重连间隔,单位毫秒
		this.heartBeatInterval = 5000; // 心跳间隔,单位毫秒
		this.pingTimeoutDuration = 1000; // 超过这个时间,后端没有返回pong,则判定后端断线了。
		this.heartBeatTimer = null;
		this.destroy = false; // 是否销毁
	}

	connect() {
		this.socket = uni.connectSocket({
			url: this.url,
			complete: () => {}
		});
		this.initEventListeners();
	}

	initEventListeners() {
		this.socket.onOpen(() => {
			// WebSocket连接已打开
			this.onConnected();
			this.startHeartBeat();
		});

		this.socket.onMessage((res) => {
			const obj = pickJsonObj(res.data);
			if (obj.type === 'pong') {
				// 收到pong消息,心跳正常,无需处理
				this.resetPingTimeout(); // 重置计时
			} else {
				// 处理其他消息
				this.onMessage(res.data);
			}
		});

		this.socket.onClose((res) => {
			// WebSocket连接已关闭
			if (this.destroy) {
				this.onClosed()
				return;
			}
			this.stopHeartBeat();
			if (!this.isReconnecting) {
				this.reconnect();
			}
		});
	}

	sendMessage(message) {
		if (this.socket) {
			this.socket.send({
				data: message
			});
		}
	}

	onMessage(message) {
		// 处理收到的消息
		console.log('message:', message)
	}

	startHeartBeat() {
		this.heartBeatTimer = setInterval(() => {
			this.sendMessage(JSON.stringify({
				type: 'ping'
			})); // 发送ping消息
			this.pingTimeout = setTimeout(() => {
				// 未收到pong消息,尝试重连...
				this.reconnect();
			}, this.pingTimeoutDuration);
		}, this.heartBeatInterval);
	}

	stopHeartBeat() {
		if (this.heartBeatTimer) {
			clearInterval(this.heartBeatTimer);
		}
	}

	reconnect() {
		this.isReconnecting = true;
		setTimeout(() => {
			this.onReconnect();
			this.connect();
			this.isReconnecting = false;
		}, this.reconnectInterval);
	}

	resetPingTimeout() {
		clearTimeout(this.pingTimeout); // 重置计时
	}

	close() {
		this.destroy = true;
		this.stopHeartBeat();
		if (this.socket) {
			this.socket.close();
			this.socket = null;
		}
	}
	/**
	 * 重连时触发
	 */
	onReconnect() {
		console.log('尝试重连...')
	}
	/**
	 * 连接成功时触发
	 */
	onConnected() {
		console.log('WebSocket连接已打开');
	}
	/**
	 * 断开时触发
	 */
	onClosed() {
		console.log('已断开连接')
	}
}

export default WebSocketClient;

使用示例

import WebSocketClient from '@/utils/websocket.js'
// 创建WebSocket实例
ws.value = new WebSocketClient('ws://这里填写你的服务地址');
// 连接WebSocket
ws.value.connect();
// 接收消息时触发
ws.value.onMessage = (value) => {
        const obj = JSON.parse(value)
        if (obj.type === 'message') {
                list.value.push({
                        time: new Date().toLocaleString(),
                        value: obj.value
                })
        }
}
// 重连时触发
ws.value.onReconnect = () => {
        netStatus.value = 2;
}
// 连接成功后触发
ws.value.onConnected = () => {
        netStatus.value = 1;
}
// 关闭后触发(直接销毁了,不会继续重连)
ws.value.onClosed = () => {
        netStatus.value = 0;
}

结语

希望对您有所帮助 😊 IMG_6150.jpeg