Bun环境下Discord网关WebSocket偶发消息接收异常问题
排查Bun环境下Discord网关WebSocket连接不稳定问题
核心问题分析
你的代码在Node.js正常但Bun下时好时坏,主要是因为Bun与Node.js在WebSocket实现细节、消息处理方式上存在差异,加上代码中存在的几个通用问题导致的。
具体修复方案
1. 处理WebSocket消息的类型差异
Bun中WebSocket的message事件参数可能是ArrayBuffer/Blob而非字符串,直接JSON.parse(e)会抛出错误,导致后续代码终止执行(无输出的情况大概率是这个原因)。需要先将消息转换为字符串:
ws.on("message", async (e) => { // 统一将消息转为字符串 const data = typeof e === 'string' ? e : await new Response(e).text(); const p = JSON.parse(data); const { t, d, op, s } = p; // ... 后续逻辑 });
2. 修正心跳包的sequence number
Discord网关要求心跳包(op=1)的d值必须是最近收到的事件序列号(s字段),而非固定的251。固定值会导致网关判定连接无效,随机断开:
let lastSequence = null; ws.on("message", async (e) => { // ... 解析数据 lastSequence = s ?? lastSequence; // 更新最近的序列号 if (op == 10) { ping(d.heartbeat_interval); console.log("interval started"); ws.send(JSON.stringify(payload)); } // ... }); function ping(ms) { return setInterval(() => { // 使用最近的序列号发送心跳 if (ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({ op: 1, d: lastSequence })); } }, ms); }
3. 添加错误与关闭事件监听
原代码没有错误处理,无法排查连接失败、发送失败的原因,加上监听可以定位问题:
ws.on("error", (err) => { console.error("WebSocket错误:", err); }); ws.on("close", (code, reason) => { console.log("连接关闭:", code, reason.toString()); });
4. 确保发送时机的可靠性
在发送身份验证 payload 前,确认WebSocket处于OPEN状态,同时可以处理send的返回值(Bun中send返回Promise):
if (op == 10) { ping(d.heartbeat_interval); console.log("interval started"); if (ws.readyState === WebSocket.OPEN) { try { await ws.send(JSON.stringify(payload)); console.log("身份验证payload已发送"); } catch (err) { console.error("发送payload失败:", err); } } }
完整修改后的代码
const ws = new WebSocket("wss://gateway.discord.gg/?v=10&encoding=json"); const TOKEN = "你的令牌"; // 替换为实际令牌 const payload = { op: 2, d: { token: TOKEN, intents: 513, properties: { os: "linux", browser: "chrome", device: "chrome", }, }, }; let lastSequence = null; let heartbeatInterval = null; ws.on("open", () => { console.log("连接已打开"); }); ws.on("error", (err) => { console.error("WebSocket错误:", err); }); ws.on("close", (code, reason) => { console.log("连接关闭:", code, reason.toString()); if (heartbeatInterval) clearInterval(heartbeatInterval); }); ws.on("message", async (e) => { try { const data = typeof e === 'string' ? e : await new Response(e).text(); const p = JSON.parse(data); const { t, d, op, s } = p; lastSequence = s ?? lastSequence; console.log(`收到op: ${op}, 事件: ${t}`); if (op === 10) { heartbeatInterval = ping(d.heartbeat_interval); console.log("心跳定时器已启动"); if (ws.readyState === WebSocket.OPEN) { await ws.send(JSON.stringify(payload)); console.log("身份验证请求已发送"); } } else if (op === 1) { // 响应网关的心跳请求 if (ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({ op: 1, d: lastSequence })); } } } catch (err) { console.error("消息处理失败:", err); } }); function ping(ms) { return setInterval(() => { if (ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({ op: 1, d: lastSequence })); console.log("发送心跳包"); } }, ms); }
额外注意事项
- Bun的WebSocket实现目前仍在迭代,部分细节与Node.js的
ws库有差异,建议关注Bun官方文档的WebSocket相关更新 - 确保你的Discord令牌权限正确,intents设置符合需求(513对应GUILDS和GUILD_MESSAGES)
内容的提问来源于stack exchange,提问作者clapmemereview
相关产品推荐
相关产品推荐

