import { ref, onMounted, onBeforeUnmount } from 'vue'; const useWebSocket = (options = {}) => { const { heartbeatInterval = 10000, //心跳是10秒一次 reconnectInterval = 3000, //重新连接间隔时间的一个参数 maxReconnectAttempts = 3, //最大重连接次数 initialReconnectInterval = 3000, // 初始重连间隔 initMessage, initMessageRetryCount = 3, // 新增:初始化消息发送重试次数 initMessageRetryInterval = 2000, // 新增:初始化消息重试间隔 maxReconnectDuration = Infinity } = options; const autoReconnect = ref(true); //是否自动重新连接 const socketTask = ref(null); const isConnected = ref(false); //表示是否已连接上。 const heartbeatTimer = ref(null); //心跳定时器 const reconnectTimer = ref(null); //重连定时器 const reconnectAttempts = ref(0); //重连的尝试次数 const isNetworkConnected = ref(true); //监听当前网络连接状态 const isManuallyClosed = ref(false); //是否是被手动关闭的 const receivedMessages = ref([]); //储从 WebSocket 服务器接收到的消息 const initMessageSendAttempts = ref(0); //初始化连接多少次 const reconnectStartTime = ref(0); //新增:记录重连开始时间 // 关闭现有连接并清理资源 const closeExistingConnection = () => { if (socketTask.value) { // 关闭 WebSocket 连接 socketTask.value.close({ success: () => { console.log('WebSocket 连接已关闭'); }, fail: (err) => { console.error('关闭 WebSocket 连接失败:', err); } }); // 清除心跳定时器 clearInterval(heartbeatTimer.value); heartbeatTimer.value = null; // 清除重连定时器 clearTimeout(reconnectTimer.value); reconnectTimer.value = null; // 标记连接已断开 isConnected.value = false; } }; // 连接 WebSocket const connect = () => { if (!isNetworkConnected.value) { uni.showToast({ title: '网络未连接...', icon: 'none' }) setTimeout(() => { uni.navigateBack() }, 1000) console.log('网络未连接,暂不尝试连接 WebSocket'); return; } // 关闭现有连接并清理资源 closeExistingConnection(); socketTask.value = uni.connectSocket({ url: uni.conf.baseUrlwws, success: (res) => { isConnected.value = true; reconnectAttempts.value = 0; // 监听初始化成功在开启心跳 startHeartbeat(); }, fail: () => { console.error('WebSocket 连接失败,尝试重连'); if (autoReconnect.value) { handleReconnect(); } } }); if (socketTask.value) { socketTask.value.onOpen(() => { // 初始化 初始购物车 sendMessage(initMessage) }); socketTask.value.onMessage((res) => { let list = JSON.parse(res.data) console.log(list, 'receivedMessages.value') receivedMessages.value.push(list); if (res.msg == 'ok') { console.log('心跳响应正常'); // 心跳正常,重置重连尝试次数 reconnectAttempts.value = 0; clearTimeout(reconnectTimer.value); } }); socketTask.value.onClose((res) => { console.log(res, 'WebSocket 连接已关闭,尝试重连'); isConnected.value = false; clearInterval(heartbeatTimer.value); // 停止心跳定时器 clearTimeout(reconnectTimer.value); // 清除重连定时器 if (res.code == '1006') { console.log('服务器正常关闭,停止重连'); uni.navigateBack() autoReconnect.value = false; return false; } if (autoReconnect.value && !isManuallyClosed.value) { handleReconnect(); } }); socketTask.value.onError((err) => { console.error('WebSocket 连接发生错误:', err); isConnected.value = false; clearInterval(heartbeatTimer.value); if (autoReconnect.value && !isManuallyClosed.value) { handleReconnect(); } }); } else { console.error('socketTask 未正确初始化'); } }; // 启动心跳机制 const startHeartbeat = () => { if (!isNetworkConnected.value) { console.log('网络未连接,暂停心跳'); uni.showToast({ title: '网络未连接...', icon: 'none' }) setTimeout(() => { uni.navigateBack() }, 1000) return; } heartbeatTimer.value = setInterval(() => { if (isConnected.value) { socketTask.value.send({ data: JSON.stringify({ type: 'ping_interval' }), success: () => { console.log('心跳消息发送成功'); const pongTimer = setTimeout(() => { console.error('心跳超时,未收到响应,尝试重连'); clearInterval(heartbeatTimer.value); if (autoReconnect) { handleReconnect(); } }, heartbeatInterval * 1.2); socketTask.value.onMessage((res) => { if (res.msg == 'ok') { clearTimeout(pongTimer); } }); }, fail: () => { console.error('心跳消息发送失败,尝试重连'); clearInterval(heartbeatTimer.value); if (autoReconnect) { handleReconnect(); } } }); } }, heartbeatInterval); }; // 手动关闭连接 const closeSocket = () => { isManuallyClosed.value = true; uni.navigateBack() closeExistingConnection(); }; // 发送消息 const sendMessage = async (data) => { if (isConnected.value) { await socketTask.value.send({ data: JSON.stringify(data), success: () => { // console.log('消息发送成功'); }, fail: () => { // console.error('消息发送失败'); } }); } else { console.error('WebSocket 未连接,无法发送消息'); } }; // 处理重连逻辑 const handleReconnect = () => { if (!isNetworkConnected.value) { console.log('网络未连接,暂停重连'); return; } if (isManuallyClosed.value) { console.log('手动关闭连接,不进行重连'); return; } if (reconnectAttempts.value < maxReconnectAttempts) { reconnectAttempts.value++; const reconnectInterval = initialReconnectInterval * Math.pow(2, reconnectAttempts.value - 1); const randomizedInterval = reconnectInterval + Math.floor(Math.random() * 1000); console.log(`尝试第 ${reconnectAttempts.value} 次重连,重连间隔: ${randomizedInterval}ms...`); reconnectTimer.value = setTimeout(() => { connect(); }, randomizedInterval); } else { console.error('重连次数达到上限,停止重连'); uni.showToast({ title: '重连次数达到上限,停止重连', icon: 'none' }); uni.navigateBack() } }; // 发送初始化消息 const sendInitMessage = async () => { if (initMessageSendAttempts.value < initMessageRetryCount) { initMessageSendAttempts.value++; await socketTask.value.send({ data: JSON.stringify(initMessage), success: () => { console.log('初始化消息发送成功'); initMessageSendAttempts.value = 0; // 重置尝试次数 }, fail: () => { console.log( `初始化消息发送失败,第 ${initMessageSendAttempts.value} 次尝试,将在 ${initMessageRetryInterval} 后重试` ); setTimeout(() => { sendInitMessage(); }, initMessageRetryInterval); } }); } else { console.error('初始化消息发送失败,已达到最大重试次数'); // initMessageSendAttempts.value = 0; // 重置尝试次数 } }; // 网络状态监听 const initNetworkListener = () => { uni.getSystemInfo({ success: (res) => { if (res.platform !== 'devtools') { uni.onNetworkStatusChange((statusRes) => { isNetworkConnected.value = statusRes.isConnected; if (statusRes.isConnected && !isManuallyClosed.value) { console.log('网络已连接,尝试重新连接 WebSocket'); if (!isConnected.value && autoReconnect.value) { connect(); } } else if (!statusRes.isConnected) { console.log('网络已断开,暂停 WebSocket 操作'); clearInterval(heartbeatTimer.value); clearTimeout(reconnectTimer.value); if (socketTask.value) { socketTask.value.close(); isConnected.value = false; } } }); } }, fail: (err) => { console.error('获取系统信息失败:', err); } }); uni.getNetworkType({ success: (res) => { isNetworkConnected.value = res.networkType !== 'none'; if (!isNetworkConnected.value) { console.log('初始网络未连接,暂不尝试连接 WebSocket'); } } }); }; onMounted(() => { initNetworkListener(); connect(); }); onBeforeUnmount(() => { closeSocket(); }); return { isConnected, sendMessage, closeSocket, receivedMessages }; }; export default useWebSocket;