You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 16:47:41