如何基于Node.js维护与Discord的WebSocket连接?
解决Discord Gateway WebSocket连接维护问题
哥们,你的推测完全正确——Discord机器人运行一段时间后挂掉,大概率是因为WebSocket连接没维护到位。你的现有代码只实现了最基础的心跳逻辑,但缺少几个核心的连接恢复机制,我帮你拆解下问题和修复方案:
你当前代码的核心不足
- 没处理Discord返回的心跳确认(OP 11):如果Discord收不到你的心跳,或者你没确认它的心跳响应,连接会被判定为失效断开。
- 没有重连机制:WebSocket随时可能因为网络波动、Discord网关重启等原因断开,你的代码没处理这种情况,断开后就彻底停了。
- 缺少会话续传逻辑:断开后直接重新发OP2鉴权会丢失会话状态,还可能触发Discord限流。
- 未捕获错误:WebSocket的error事件没处理,一旦出未捕获错误,进程直接崩溃。
修复后的完整代码
const WebSocket = require('ws'); const botToken = '你的机器人Token'; let ws; let interval; let sessionId = null; let sequenceNumber = null; const gatewayUrl = 'wss://gateway.discord.gg/?v=6&encoding=json'; // 创建连接的核心函数 function createConnection() { ws = new WebSocket(gatewayUrl); ws.on('open', () => { console.log('Gateway连接已建立'); // 根据是否有会话ID,决定是续传会话还是首次鉴权 const payload = sessionId ? { op: 6, // 会话续传OP码 d: { token: botToken, session_id: sessionId, seq: sequenceNumber } } : { op: 2, // 首次鉴权OP码 d: { token: botToken, intents: 32767, properties: { $os: 'linux', $browser: 'custom-bot', $device: 'custom-bot' } } }; ws.send(JSON.stringify(payload)); }); ws.on('message', (data) => { const response = JSON.parse(data); const { op, d, t, s } = response; // 更新序列号,用于会话续传 if (s !== null) { sequenceNumber = s; } switch (op) { case 10: // 心跳间隔通知 const { heartbeat_interval } = d; // 提前1秒发送心跳,留足网络缓冲时间 interval = setInterval(() => { ws.send(JSON.stringify({ op: 1, d: sequenceNumber })); console.log('发送心跳'); }, heartbeat_interval - 1000); break; case 11: // 心跳确认 console.log('收到心跳确认'); // 重置心跳计时器,避免累计延迟 clearInterval(interval); interval = setInterval(() => { ws.send(JSON.stringify({ op: 1, d: sequenceNumber })); console.log('发送心跳'); }, response.d.heartbeat_interval - 1000); break; case 0: // 普通事件 if (t === 'READY') { // 保存会话ID,用于后续重连续传 sessionId = d.session_id; console.log(`机器人已就绪,会话ID:${sessionId}`); } break; case 9: // 无效会话,需重新鉴权 console.log('会话无效,将重新鉴权'); sessionId = null; sequenceNumber = null; clearInterval(interval); setTimeout(createConnection, 5000); // 延迟5秒重连,避免限流 break; case 7: // Discord要求重新连接网关 console.log('收到重新连接通知'); clearInterval(interval); setTimeout(createConnection, 1000); break; } }); ws.on('close', (code, reason) => { console.log(`连接断开,代码:${code},原因:${reason.toString()}`); clearInterval(interval); // 非正常关闭(代码≠1000)时尝试重连 if (code !== 1000) { // 指数退避重连,避免频繁请求被限流(延迟1s~30s随机递增) const delay = Math.min(1000 * Math.pow(2, Math.floor(Math.random() * 5)), 30000); console.log(`将在${delay}毫秒后尝试重连`); setTimeout(createConnection, delay); } }); ws.on('error', (error) => { console.error('WebSocket错误:', error); clearInterval(interval); }); } // 启动连接 createConnection();
关键修复点说明
- 稳定心跳管理:不仅发送心跳,还处理Discord的OP11确认,并重置计时器,避免累计延迟导致超时。
- 会话续传:保存
session_id和sequenceNumber,断开后优先用OP6续传会话,而非重新鉴权,既保留状态又符合Discord规范。 - 智能重连策略:监听
close事件,根据断开代码判断是否重连,用指数退避延迟避免触发限流。 - 处理特殊OP码:针对Discord网关的OP7(强制重连)、OP9(无效会话)等事件做专门处理,确保连接能自动恢复。
- 错误捕获:监听
error事件,避免未捕获错误导致进程直接崩溃。
额外注意事项
- 确保机器人Token正确,且已开启所需的intents(你用的32767是所有非特权intents,没问题)。
- 不要频繁手动重启连接,Discord有限流机制,短时间多次重连可能被暂时封禁网关。
- 可以添加更详细的日志,方便排查连接异常问题。
内容的提问来源于stack exchange,提问作者BlackdestinyXX
相关产品推荐
相关产品推荐

