问题修复

This commit is contained in:
2025-12-05 19:22:28 +08:00
parent 2c2a87ab19
commit 7fdabf1507
10 changed files with 442 additions and 345 deletions

View File

@@ -1,157 +1,241 @@
import {
reactive,
ref
} from 'vue';
import { reactive, ref } from 'vue';
// WebSocket 工具类,封装了 WebSocket 的连接、发送、接收、心跳、重连和关闭等操作
class WebsocketUtil {
// 构造函数,初始化 WebSocket 连接
constructor(url, time, params) {
this.url = url; // WebSocket 服务器的 URL
this.params = params; // WebSocket 服务器的 URL
this.time = time; // 心跳发送的间隔时间(秒)
this.socketTask = null; // WebSocket 任务对象
this.isOpen = false; // WebSocket 连接是否打开
this.reconnectTimeout = null; // 重连定时器
this.heartbeatInterval = null; // 心跳定时器
this.messageCallbacks = []; // 存储外部注册的消息回调函数的数组
this.messageCallbacks = []; // 存储外部注册的消息回调函数的数组
// 构造函数,初始化 WebSocket 连接
constructor(url, time, params) {
this.url = url; // WebSocket 服务器的 URL
this.params = params; // WebSocket 连接参数
this.time = time; // 心跳发送的间隔时间(秒)
this.socketTask = null; // WebSocket 任务对象
this.isOpen = false; // WebSocket 连接是否打开
// 初始化 WebSocket 连接
this.initializeWebSocket();
}
// 定时器相关
this.reconnectTimeout = null; // 重连定时器
this.heartbeatInterval = null; // 心跳定时器
this.heartbeatTimeout = null; // 心跳超时定时器(检测 pong 响应)
this.checkConnectionInterval = null; // 连接状态主动检查定时器
// 初始化 WebSocket 连接
initializeWebSocket() {
console.log('初始化WebSocket连接');
if (this.isOpen) {
return
}
this.socketTask = uni.connectSocket({
url: this.url,
success: () => {
console.log('WebSocket连接成功');
return this.socketTask;
},
fail: (error) => {
console.error('WebSocket连接失败', error);
uni.$emit('is-socket-open', true)
this.reconnect();
}
});
// 消息回调数组
this.messageCallbacks = []; // 存储外部注册的消息回调函数的数组
this.socketTask.onOpen((res) => {
console.log('WebSocket连接正常==', res);
this.isOpen = true;
// 连接成功后启动心跳和消息监听
this.startHeartbeat();
this.listenForMessages();
uni.$emit('is-socket-open', true)
// 重连策略配置
this.reconnectAttempts = 0; // 当前重连次数
this.reconnectMaxAttempts = 5; // 最大重连次数(设为 Infinity 表示无限重试)
this.reconnectDelay = 3000; // 基础重连延迟(毫秒)
this.maxReconnectDelay = 30000; // 最大重连延迟(毫秒)
});
this.socketTask.onError((res) => {
console.log('WebSocket连接失败==', res);
uni.$emit('is-socket-open', false)
this.reconnect();
});
// 注意:这里的 onClose 监听器应该放在 uni.connectSocket 调用之后
this.socketTask.onClose((result) => {
this.isOpen = false;
// if( this.isOpen ){
this.reconnect();
// }
});
// 初始化 WebSocket 连接
this.initializeWebSocket();
}
// 监听设备唤醒事件(浏览器环境)
if (typeof document !== 'undefined') {
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible') {
// 页面可见时,主动检查连接
this.checkConnection();
}
});
}
}
// 启动心跳检测
startHeartbeat() {
if (this.heartbeatInterval) {
clearInterval(this.heartbeatInterval);
}
this.heartbeatInterval = setInterval(() => {
if (this.isOpen) {
this.send(JSON.stringify({"type": "ping_interval","set": "pad"}));
}
}, this.time);
}
// 初始化 WebSocket 连接
initializeWebSocket() {
console.log('初始化WebSocket连接');
if (this.isOpen) {
return;
}
// 发送消息
send(data, type) {
if (this.socketTask && this.isOpen) {
this.socketTask.send({
data: data,
success: (res) => {
// console.log('消息发送成功', res);
this.startHeartbeat();
},
fail: (error) => {
console.error('消息发送失败', error);
this.reconnect(); // 这里可能需要根据实际情况判断是否重连
}
});
}
}
this.socketTask = uni.connectSocket({
url: this.url,
success: () => {
console.log('WebSocket连接请求发送成功');
},
fail: (error) => {
console.error('WebSocket连接失败', error);
uni.$emit('is-socket-open', false);
this.reconnect();
}
});
// 监听 WebSocket 消息
listenForMessages() {
if (this.socketTask) {
this.socketTask.onMessage((res) => {
const {
data
} = res;
this.messageCallbacks.forEach(callback => callback(data
.toString())); // 假设 data 是字符串或可转换为字符串
});
// this.send("WebSocket连接正常");
} else {
console.error('WebSocket 连接尚未建立,无法监听消息');
}
}
// 连接打开事件
this.socketTask.onOpen((res) => {
console.log('WebSocket连接正常==', res);
this.isOpen = true;
// 重置重连状态
this.reconnectAttempts = 0;
this.reconnectDelay = 3000;
// 启动心跳和消息监听
this.startHeartbeat();
this.listenForMessages();
uni.$emit('is-socket-open', true);
});
// 重连 WebSocket
reconnect() {
if (this.reconnectTimeout) {
clearTimeout(this.reconnectTimeout);
}
this.reconnectTimeout = setTimeout(() => {
this.initializeWebSocket();
}, 3000);
}
// 连接错误事件
this.socketTask.onError((res) => {
console.log('WebSocket连接错误==', res);
uni.$emit('is-socket-open', false);
this.reconnect();
});
// 关闭 WebSocket 连接
closeSocket() {
if (this.socketTask) {
uni.closeSocket({
success: () => {
console.log('WebSocket连接已关闭');
this.isOpen = false;
},
fail: (error) => {
console.error('关闭WebSocket连接失败', error);
}
});
this.socketTask = null;
}
}
// 连接关闭事件
this.socketTask.onClose((result) => {
console.log('WebSocket连接已关闭', result);
this.isOpen = false;
this.reconnect();
});
// 外部注册消息回调函数
onMessage(callback) {
this.messageCallbacks.push(callback);
}
// 启动连接状态主动检查30秒一次
this.startConnectionCheck();
}
// 外部注销消息回调函数
offMessage(callback) {
this.messageCallbacks = []
}
// 启动心跳检测
startHeartbeat() {
// 清除现有定时器
if (this.heartbeatInterval) clearInterval(this.heartbeatInterval);
if (this.heartbeatTimeout) clearTimeout(this.heartbeatTimeout);
// 销毁 WebSocket 连接,清理资源
destroy() {
this.closeSocket();
clearInterval(this.heartbeatInterval);
clearTimeout(this.reconnectTimeout);
this.messageCallbacks = [];
}
this.heartbeatInterval = setInterval(() => {
if (this.isOpen) {
// 发送心跳包
this.send(JSON.stringify({ "type": "ping_interval", "set": "pad" }));
// 设置5秒超时若未收到pong则重连
this.heartbeatTimeout = setTimeout(() => {
console.log('心跳超时,主动断开并重连');
this.closeSocket();
this.reconnect();
}, 5000);
}
}, this.time);
}
// 启动连接状态主动检查30秒一次
startConnectionCheck() {
if (this.checkConnectionInterval) clearInterval(this.checkConnectionInterval);
this.checkConnectionInterval = setInterval(() => {
if (!this.isOpen && !this.reconnectTimeout) {
console.log('主动检查到连接断开,触发重连');
this.reconnect();
}
}, 30000);
}
// 发送消息
send(data, type) {
if (this.socketTask && this.isOpen) {
this.socketTask.send({
data: data,
success: (res) => {
// 发送成功,重置心跳(可选,根据业务需求)
this.startHeartbeat();
},
fail: (error) => {
console.error('消息发送失败', error);
this.reconnect();
}
});
} else {
console.warn('WebSocket未连接无法发送消息');
}
}
// 监听 WebSocket 消息
listenForMessages() {
if (this.socketTask) {
this.socketTask.onMessage((res) => {
const data = res.data.toString();
try {
// 尝试解析JSON处理pong响应
const message = JSON.parse(data);
if (message.type === 'pong') {
// 收到pong清除心跳超时
if (this.heartbeatTimeout) clearTimeout(this.heartbeatTimeout);
}
} catch (e) {
console.warn('消息解析失败非JSON格式:', e);
}
// 触发外部注册的回调函数
this.messageCallbacks.forEach(callback => callback(data));
});
} else {
console.error('WebSocket 连接尚未建立,无法监听消息');
}
}
// 重连 WebSocket指数退避策略
reconnect() {
if (this.reconnectTimeout) clearTimeout(this.reconnectTimeout);
// 达到最大重连次数,停止重试(可根据需求调整为无限重试)
if (this.reconnectAttempts >= this.reconnectMaxAttempts) {
console.log(`已达最大重连次数 ${this.reconnectMaxAttempts},停止重连`);
return;
}
// 指数退避:延迟时间 = 基础延迟 * 2^重连次数最大不超过maxReconnectDelay
const delay = Math.min(
this.reconnectDelay * Math.pow(2, this.reconnectAttempts),
this.maxReconnectDelay
);
this.reconnectTimeout = setTimeout(() => {
this.reconnectAttempts++;
console.log(`重连尝试 ${this.reconnectAttempts}/${this.reconnectMaxAttempts},延迟 ${delay}ms`);
this.initializeWebSocket();
}, delay);
}
// 主动检查连接状态
checkConnection() {
if (!this.isOpen && !this.reconnectTimeout) {
this.reconnect();
}
}
// 关闭 WebSocket 连接
closeSocket() {
if (this.socketTask) {
uni.closeSocket({
success: () => {
console.log('WebSocket连接已关闭');
this.isOpen = false;
},
fail: (error) => {
console.error('关闭WebSocket连接失败', error);
}
});
this.socketTask = null;
}
}
// 外部注册消息回调函数
onMessage(callback) {
this.messageCallbacks.push(callback);
}
// 外部注销消息回调函数
offMessage(callback) {
// 若传入callback则移除指定回调否则清空所有回调
if (callback) {
this.messageCallbacks = this.messageCallbacks.filter(cb => cb !== callback);
} else {
this.messageCallbacks = [];
}
}
// 销毁 WebSocket 连接,清理资源
destroy() {
this.closeSocket();
// 清除所有定时器
clearInterval(this.heartbeatInterval);
clearTimeout(this.heartbeatTimeout);
clearTimeout(this.reconnectTimeout);
clearInterval(this.checkConnectionInterval);
// 清空回调数组
this.messageCallbacks = [];
console.log('WebSocket资源已销毁');
}
}
export default WebsocketUtil;