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

Discord WebSocket连接语音频道后自动断开问题求助

Discord WebSocket语音频道连接自动断开问题

我按照Discord官方文档实现WebSocket连接以加入语音频道,但连接会在30-40秒后无报错自动断开。已设置10秒ping间隔并配置对应ping payload,问题仍未解决。

我的代码

import json
import time
from websocket import WebSocket
from websocket import WebSocketApp

class ConnectWebSocket(WebSocketApp):
    """Class Inherited From `WebSocketApp`"""
    def __init__(
            self,
            token: str,
            guild_id: str,
            channel_id: str
        ):
        super().__init__(
            header=[
                "Accept-Language: en-US",
                "Connection: Upgrade",
                "Host: gateway.discord.gg",
                "Sec-Websocket-Extensions: permessage-deflate; client_max_window_bits",
                "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) discord/1.0.9153 Chrome/124.0.6367.243 Electron/30.1.0 Safari/537.36"
            ],
            url="wss://gateway.discord.gg/?v=9&encoding=json",
            on_open=self.on_connection,
            on_message=self.on_message
        )
        self._token = token
        self._guild = guild_id
        self._channel = channel_id

    def on_connection(self, ws):
        print("Opened a connection...")
        ws.send(json.dumps({
            "op": 2,
            "d": {
                "token": self._token,
                "intents": 513,
                "properties": {
                "os": "windows 10",
                "browser": "Google Chrome",
                "device": "Windows"
                }
            }
        }))
        print("First Payload Sent")
        ws.send(json.dumps({
                "op": 4,
                "d": {
                    "guild_id": self._guild,
                    "channel_id": self._channel,
                    "self_mute": False,
                    "self_deaf": False
                }
            }
        ))
        print("Second Payload Sent")

    def on_message(self, ws, message):
        print(f"Received message")

    def run_forever(self, proxy):
        username_password, proxy_address_port = proxy.split('@')
        username, password = username_password.split(':')
        proxy_address, port = proxy_address_port.split(':')
        return super().run_forever(
            reconnect=5,
            ping_interval=10,
            proxy_type="http",
            http_proxy_host=proxy_address,
            http_proxy_port=int(port),
            http_proxy_auth=(username, password),
            ping_payload={"op": 6, "d": int(time.time() * 1000)},
        )
    
    def on_error(self, ws, error):
        print(error)

    def on_close(self, ws, close_status_code, close_msg):
        print("Closed con")

问题排查与修复方案

1. 错误的心跳机制实现

Discord Gateway的心跳规则不是客户端主动发送OP 6,而是:

  • 服务器会先发送OP 10(Hello)包,包含heartbeat_interval字段,客户端需要按照这个间隔发送OP 1心跳包
  • 服务器会主动发送OP 1心跳请求,客户端必须回复相同的OP 1和对应时间戳

你硬编码的10秒间隔和OP 6格式不符合Discord规范,这是连接断开的核心原因。

2. 未处理关键Gateway事件

你的on_message只打印收到消息,完全忽略了维持连接必需的事件:

  • OP 10(Hello):获取心跳间隔,启动心跳定时器
  • OP 1(心跳请求):回复心跳以维持连接
  • OP 0(事件):处理语音状态更新等关键事件

3. 修复后的代码

import json
import time
import threading
from websocket import WebSocketApp

class ConnectWebSocket(WebSocketApp):
    def __init__(self, token: str, guild_id: str, channel_id: str):
        super().__init__(
            header=[
                "Accept-Language: en-US",
                "Connection: Upgrade",
                "Host: gateway.discord.gg",
                "Sec-Websocket-Extensions: permessage-deflate; client_max_window_bits",
                "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) discord/1.0.9153 Chrome/124.0.6367.243 Electron/30.1.0 Safari/537.36"
            ],
            url="wss://gateway.discord.gg/?v=9&encoding=json",
            on_open=self.on_connection,
            on_message=self.on_message,
            on_error=self.on_error,
            on_close=self.on_close
        )
        self._token = token
        self._guild = guild_id
        self._channel = channel_id
        self.heartbeat_interval = None
        self.heartbeat_thread = None
        self.last_sequence = None

    def _send_heartbeat(self):
        """定时发送心跳包"""
        while self.heartbeat_interval and self.sock.connected:
            self.send(json.dumps({"op": 1, "d": self.last_sequence}))
            time.sleep(self.heartbeat_interval / 1000)

    def on_connection(self, ws):
        print("连接已建立...")
        # 发送身份验证Payload
        ws.send(json.dumps({
            "op": 2,
            "d": {
                "token": self._token,
                "intents": 513,
                "properties": {
                    "os": "windows 10",
                    "browser": "Google Chrome",
                    "device": "Windows"
                }
            }
        }))
        print("身份验证Payload已发送")

    def on_message(self, ws, message):
        data = json.loads(message)
        op = data.get("op")
        self.last_sequence = data.get("s")

        if op == 10:
            # 处理Hello包,获取心跳间隔
            self.heartbeat_interval = data["d"]["heartbeat_interval"]
            print(f"收到Hello包,心跳间隔: {self.heartbeat_interval}ms")
            # 启动心跳线程
            self.heartbeat_thread = threading.Thread(target=self._send_heartbeat, daemon=True)
            self.heartbeat_thread.start()
            # 心跳启动后再发送语音频道加入请求
            ws.send(json.dumps({
                "op": 4,
                "d": {
                    "guild_id": self._guild,
                    "channel_id": self._channel,
                    "self_mute": False,
                    "self_deaf": False
                }
            }))
            print("语音频道加入Payload已发送")
        elif op == 1:
            # 响应服务器的心跳请求
            ws.send(json.dumps({"op": 1, "d": data["d"]}))
            print("发送心跳响应")
        elif op == 0:
            # 处理事件,比如语音状态更新
            print(f"收到事件: {data['t']}")

    def run_forever(self, proxy):
        username_password, proxy_address_port = proxy.split('@')
        username, password = username_password.split(':')
        proxy_address, port = proxy_address_port.split(':')
        return super().run_forever(
            reconnect=5,
            proxy_type="http",
            http_proxy_host=proxy_address,
            http_proxy_port=int(port),
            http_proxy_auth=(username, password),
            # 移除错误的ping配置,改用自定义心跳逻辑
        )
    
    def on_error(self, ws, error):
        print(f"错误: {error}")

    def on_close(self, ws, close_status_code, close_msg):
        print(f"连接关闭,状态码: {close_status_code},消息: {close_msg}")

关键修复说明

  • 遵循Discord Gateway规范,使用OP 1心跳包替代OP 6
  • 根据服务器返回的heartbeat_interval动态调整心跳间隔,不再硬编码
  • 处理Hello包后再发送语音频道加入请求,保证连接初始化完成
  • 跟踪last_sequence参数,这是维持连接和重连的必要数据

内容的提问来源于stack exchange,提问作者mohammad hassan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 18:40:55