let connected = false; // socket 是否连接 let socket = null; // websocket 实例 let lockSocket = false; let socketMsgQueue = []; // socket消息队列 let prevTimestamp = 0; // 上次收到消息的时间戳 let sendHeartTimer = null; // 💓的timer计时器 let ws = null; initSocket(); function initSocket() { if (lockSocket) return; lockSocket = true; ws = new WebSocket('wss://www.tall.wiki/websocket/recovery/recovery/ws'); ws.onopen = onOpen; ws.onmessage = onMessage; ws.onclose = onClose; lockSocket = false; } // ws 打开 function onOpen(event) { connected = true; prevTimestamp = Date.now(); auth(); // 认证 for (let i = 0; i < socketMsgQueue.length; i += 1) { send(socketMsgQueue[i]); } socketMsgQueue = []; } // ws收到消息 function onMessage(res) { try { prevTimestamp = Date.now(); // console.warn('message: ', res, new Date().toLocaleString()); if (res && res.data && JSON.parse(res.data)) { const resData = JSON.parse(res.data); const { messageSet, ackId } = resData; // 处理消息体对象 messageSet.forEach(item => handleMessagesData(item)); // console.log('ackId', ackId); // 有ackId 发送ack ackId && send({ type: 'Ack', data: { ackId } }); } } catch (error) { console.error(error); } } /** * 处理收到的消息内容 * @param {object} item 单个消息体对象 */ function handleMessagesData(item) { const data = JSON.parse(item.data); switch (data.type) { case 'ChannelStatus': // 认证消息 handleAuthMessage(data); break; case 'feedback': // 收到拔萝卜的动作反馈 console.log('score', data.data.score) data.data && data.data.result === 1 && handlePull(data.data.times); onScoreMessage(data.data.score, data.data.times); break; case 'startDrill': // 开始训练游戏 console.log(data.data); window.gameInfo = data.data; initBeginCount(); break; case 'countdown': onCountdownMessage(); break; case 'score': onScoreMessage(data.data.score, data.data.times); break; default: break; } } // ws 关闭 function onClose(event) { connected = false; if (sendHeartTimer) clearInterval(sendHeartTimer); // console.warn('close:', connected, new Date().toLocaleString()); if (!event || event.code !== 1005) { socket.close(); } setTimeout(() => { // connected 在这里的作用是: // 在发生重连 但是还没连上之前 不要再次重连 initSocket(); }, 300); } /** * 发送消息 * @param data */ function send(data) { if (connected) { if (ws.readyState === 1) { ws.send(JSON.stringify({ toDomain: 'Server', data: JSON.stringify(data) })); } } else { socketMsgQueue.push(data); } } // 认证消息 function auth() { // TODO: userId拿父框架传递过来的 const data = { type: 'Auth', data: { userId: '1399952569681448962' } }; send(data); } // 心跳检测 function sendHeart() { if (sendHeartTimer) clearInterval(sendHeartTimer); sendHeartTimer = setInterval(() => { if (Date.now() - prevTimestamp >= 15000) { send({ type: 'Ping' }); if (Date.now() - prevTimestamp > 20000) { // console.warn('手动断开'); ws.close(); } } }, 5000); } /** * 处理auth认证返回的ChannelStatus消息 * @param {object} data 消息内容对象 */ function handleAuthMessage(data) { if (data.data.authed) { // 认证成功 sendHeart(); } else { console.error('消息系统认证失败'); // 清除掉本地无用的token ws = null; } }