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, onMessage } = 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 isPongReceived = ref(false) const allowReconnect = ref(true); // 新增:控制是否允许重连 // 关闭现有连接并清理资源 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; } }; const websocketsendMessage = (data) => { uni.$u.debounce(sendMessage(data), 500) } // 连接 WebSocket const connect = (connectMsg) => { if (!isNetworkConnected.value) { uni.showToast({ title: '网络未连接...', icon: 'none' }) setTimeout(() => { uni.pro.switchTab('index/index') }, 1000) console.log('网络未连接,暂不尝试连接 WebSocket'); return; } // 关闭现有连接并清理资源 try{ closeExistingConnection(); }catch(err){ } socketTask.value = uni.connectSocket({ url: uni.conf.baseUrlwws, success: (res) => { isConnected.value = true; // 监听初始化成功在开启心跳 startHeartbeat(); }, fail: () => { console.error('WebSocket 连接失败,尝试重连'); if (autoReconnect.value && allowReconnect.value) { handleReconnect(); } } }); if (socketTask.value) { socketTask.value.onOpen(() => { // 初始化 初始购物车 sendMessage(connectMsg ? connectMsg : initMessage) }); socketTask.value.onMessage((res) => { receivedMessages.value = JSON.parse(res.data) websocketsendMessage({ type: 'receipt', msg_id: receivedMessages.value.msg_id }) if (onMessage) { onMessage(receivedMessages.value) } // receivedMessages.value.push(list); if (receivedMessages.value == 'ok' || receivedMessages.value.operate_type == 'init') { console.log('初始化正常,心跳响应正常'); // 清除重连定时器 clearTimeout(reconnectTimer.value); allowReconnect.value = false reconnectTimer.value = null; } }); socketTask.value.onClose((res) => { console.log(res, 'WebSocket 连接已关闭,尝试重连'); isConnected.value = false; clearInterval(heartbeatTimer.value); // 停止心跳定时器 clearTimeout(reconnectTimer.value); // 清除重连定时器 if (res.code == '1006' && !allowReconnect.value) { uni.showToast({ title: '网络异常,请重新扫码', icon: 'none' }); autoReconnect.value = false; setTimeout(() => { uni.pro.switchTab('index/index'); }, 1000) 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.pro.switchTab('index/index'); }, 1000); return; } heartbeatTimer.value = setInterval(() => { if (isConnected.value) { console.log('发送心跳消息'); isPongReceived.value = false; // 每次发送心跳消息前重置标记 socketTask.value.send({ data: JSON.stringify({ type: 'ping_interval', set: 'shopping' }), success: () => { console.log('心跳消息发送成功'); const pongTimer = setTimeout(() => { if (!isPongReceived.value) { console.error('心跳超时,未收到响应,尝试重连'); clearInterval(heartbeatTimer.value); if (autoReconnect.value && reconnectAttempts.value < maxReconnectAttempts && allowReconnect.value) { handleReconnect(); } else { console.error('重连次数达到上限,停止重连和心跳'); clearInterval(heartbeatTimer.value); autoReconnect.value = false; uni.pro.switchTab('index/index'); } } }, heartbeatInterval * 1.2); const handlePong = (res) => { try { let data = JSON.parse(res.data); if (data.operate_type == "init" || (data.msg === 'ok' && data.msg_id == 'ping_interval')) { isPongReceived.value = true; console.log('收到心跳响应,清除超时定时器'); clearTimeout(pongTimer); } } catch (error) { console.error('解析心跳响应数据时出错:', error); } }; socketTask.value.onMessage(handlePong); }, fail: () => { console.error('心跳消息发送失败,尝试重连'); clearInterval(heartbeatTimer.value); if (autoReconnect.value && reconnectAttempts.value < maxReconnectAttempts && allowReconnect.value) { handleReconnect(); } else { console.error('重连次数达到上限,停止重连和心跳'); clearInterval(heartbeatTimer.value); autoReconnect.value = false; uni.pro.switchTab('index/index'); } } }); } }, heartbeatInterval); }; // 手动关闭连接 const closeSocket = () => { isManuallyClosed.value = true; 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 (!allowReconnect.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); uni.showLoading({ title: `正在努力连接..`, mask: true }) console.log(`尝试第 ${reconnectAttempts.value} 次重连,重连间隔: ${randomizedInterval}ms...`); reconnectTimer.value = setTimeout(() => { connect(); }, randomizedInterval); } else { console.error('重连次数达到上限,停止重连'); uni.showToast({ title: '重连次数达到上限,停止重连', icon: 'none' }); clearInterval(heartbeatTimer.value); autoReconnect.value = false; setTimeout(() => { uni.hideLoading(); uni.pro.switchTab('index/index'); }, 1000) } }; // / 网络状态监听 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'); } } }); }; // 页面显示,尝试连接 WebSocket const onShowconnect = () => { if (autoReconnect.value) { uni.showLoading({ title: `尝试再次连接`, mask: true }) connect(); } } onBeforeUnmount(() => { closeSocket(); }); return { isConnected, sendMessage, closeSocket, receivedMessages, closeExistingConnection, connect, onShowconnect, initNetworkListener, connect,allowReconnect }; }; export default useWebSocket;