Discord Websocket保持连接但一段时间后停止接收消息事件
问题分析与修复方案
你的问题核心是Discord网关会话进入无效状态,但心跳机制仍在运行,导致连接看似正常但无法接收事件。以下是代码中的关键问题及修复方案:
1. 未处理网关关键控制指令
Discord网关会通过特定OP码通知客户端状态变更,你的代码缺少对这些指令的处理:
- OP 7 (Reconnect):网关要求客户端主动重连,若不处理会导致后续事件丢失
- OP 9 (Invalid Session):当前会话已失效,需重新发起Identify而非Resume
2. 心跳定时器未清理
每次重连时,旧的心跳定时器未被清除,导致多个心跳任务同时运行,干扰网关状态。
3. Resume逻辑边界缺失
当会话过期时仍尝试Resume,会陷入无效循环,需在Invalid Session时重置会话信息,重新走Identify流程。
修复后的代码
const WebSocket = require('ws'); const listenerjs = require("./listener.js"); const initialURL = "wss://gateway.discord.gg"; const userToken = "Token"; let url = initialURL, sessionID = ""; let ws; let interval = 0, seq = -1; const payload = { op: 2, d: { token: userToken, intents: 33280, properties: { $os: "windows", $browser: "chrome", $device: "chrome", } } } const heartbeat = (ms) => { return setInterval(() => { if (ws?.readyState === WebSocket.OPEN) { console.log("Heartbeat Sent!") ws.send(JSON.stringify({ op: 1, d: seq })); } }, ms); }; const clearHeartbeat = () => { if (interval) { clearInterval(interval); interval = 0; } }; const initializeWebsocket = () => { // 清理旧连接和定时器 clearHeartbeat(); if (ws && ws.readyState !== WebSocket.CLOSED) { ws.close(1000, "Reconnecting"); } let wasReady = false; ws = new WebSocket(url + "/?v=10&encoding=json"); ws.on("open", function open() { if (url !== initialURL && sessionID && seq !== -1) { console.log("Attempting to resume session"); const resumePayload = { op: 6, d: { token: userToken, session_id: sessionID, seq: seq } } ws.send(JSON.stringify(resumePayload)) } else { // 首次连接或会话失效,发起Identify ws.send(JSON.stringify(payload)) } }); ws.on("error", function error(e){ console.error("Gateway error:", e); }); ws.on("close", function close(code, reason){ if (wasReady) { console.log(`Gateway closed (${code}): ${reason}, reconnecting in 7.5s`); } setTimeout(() => { initializeWebsocket(); }, 7500); }); ws.on("message", function incoming(data){ let p = JSON.parse(data); const { t, op, d, s } = p; // 更新序列值(所有带s的包都要更新) if (s !== undefined) { seq = s; } switch(op){ case 0: // 普通事件,已通过上面更新seq break; case 7: // 网关要求重连 console.log("Gateway requested reconnect"); initializeWebsocket(); break; case 9: // 无效会话,重置信息重新Identify console.log("Invalid session, resetting and re-identifying"); sessionID = ""; seq = -1; url = initialURL; initializeWebsocket(); break; case 10: const { heartbeat_interval } = d; clearHeartbeat(); // 确保没有旧定时器 interval = heartbeat(heartbeat_interval); wasReady = true; break; case 11: const today = new Date(); const dateTime = `${today.getFullYear()}-${String(today.getMonth()+1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')} ${String(today.getHours()).padStart(2, '0')}:${String(today.getMinutes()).padStart(2, '0')}:${String(today.getSeconds()).padStart(2, '0')}`; console.log(`Heartbeat Received! ${dateTime}`); break; } switch(t){ case "READY": console.log("Gateway Connection Ready!"); url = d.resume_gateway_url; sessionID = d.session_id; break; case "RESUMED": console.log("Gateway Connection Resumed"); break; case "MESSAGE_CREATE": listenerjs.listener(d); break; } }) } initializeWebsocket();
关键修复点说明
- 新增OP 7和OP 9处理:主动响应网关的重连要求,会话失效时重置状态重新初始化
- 心跳定时器清理:每次重连前清除旧定时器,避免重复发送心跳
- 完善Resume条件:只有当sessionID和seq有效时才尝试Resume,否则直接Identify
- 心跳包携带seq:按Discord规范携带当前seq,帮助网关确认会话状态
- 增强日志与错误处理:便于排查连接状态问题
内容的提问来源于stack exchange,提问作者LordTakahiro
相关产品推荐
相关产品推荐

