You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Python WebSocket服务器连接异常问题求助

解决你的WebSocket服务器连接与异常问题

你现在遇到的WebSocket服务器问题主要集中在握手超时、客户端感知连接滞后,以及异常断开报错上,咱们一步步拆解问题、解决疑问并修复代码。


你的疑问解答

1. 使用子协议是否有用?如何在Python服务器配置?

子协议主要用于在WebSocket连接上层定义自定义通信规范(比如指定JSON/Protobuf数据格式),对解决当前的握手超时问题帮助不大,但如果后续需要规范客户端与服务器的通信格式可以配置。在websockets库中配置很简单:

  • 服务器端修改websockets.serve,添加subprotocols参数:
start_server = websockets.serve(handle_connection, "", 5678, subprotocols=["my-custom-protocol"])
  • 客户端创建WebSocket时指定对应子协议:
var ws = new WebSocket("ws://86.205.245.32:5678/", "my-custom-protocol");

2. 使用WSS是否有帮助?

WSS(WebSocket over TLS)是加密的WebSocket连接,本身不能直接解决握手超时,但如果你的网络环境对未加密的WS连接有拦截或限制,切换到WSS可能绕过这类防护。配置WSS需要SSL证书,你可以先用自签名证书测试(生产环境建议使用可信CA颁发的证书):

  • 服务器端添加SSL配置:
import ssl
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ssl_context.load_cert_chain(certfile="cert.pem", keyfile="key.pem")
start_server = websockets.serve(handle_connection, "", 5678, ssl=ssl_context)
  • 客户端连接改为wss://开头的地址,注意自签名证书在浏览器会触发安全提示,需要手动信任。

3. 是否需要将绑定IP从0.0.0.0改为其他地址?

绑定""等价于0.0.0.0,表示监听服务器所有网卡的请求,这不是你当前问题的根源。除非你的树莓派有多个网卡,你想指定只监听某个特定IP,否则不需要修改。如果担心安全,可以绑定树莓派的内网IP(比如192.168.x.x),但外网客户端需要能通过端口转发访问到这个IP,保持0.0.0.0会更灵活。


代码核心问题修复

你的服务器代码有个致命逻辑问题:time异步函数在连接建立后直接阻塞在await websocket.recv(),如果客户端没有及时发送消息,整个连接处理会卡住,导致浏览器认为握手超时,这也是客户端只有在服务器停止时才感知连接的原因。同时,原代码没有处理连接断开的异常,导致服务器报错。

修复后的服务器代码

print("start serveur")
import websockets, asyncio

async def handle_connection(websocket, path):
    print("New client connected")
    # 握手完成后主动发送确认消息,触发客户端onopen事件
    await websocket.send("Connection established")
    try:
        # 循环接收消息,避免单次recv阻塞连接
        while True:
            message = await websocket.recv()
            print(f"Received message: {message}")
            # 可选:回复客户端消息
            await websocket.send(f"Echo: {message}")
    except websockets.exceptions.ConnectionClosed:
        print("Client disconnected gracefully")

start_server = websockets.serve(handle_connection, "", 5678)
loop = asyncio.get_event_loop()
loop.run_until_complete(start_server)
print("Server running on port 5678")
loop.run_forever()

优化后的客户端代码(增加错误与消息处理)

<script>
alert("Initializing WebSocket...");
var ws = new WebSocket("ws://86.205.245.32:5678/");

ws.onopen = function (event) {
    alert("Handshake successful! Connection established");
    ws.send("Hello from client");
};

ws.onmessage = function(event) {
    console.log("Received from server: ", event.data);
};

ws.onerror = function(error) {
    console.error("WebSocket error occurred: ", error);
};

ws.onclose = function(event) {
    console.log("Connection closed. Code: ", event.code, " Reason: ", event.reason);
};
</script>

错误原因与环境配置建议

错误原因分析

  1. 握手超时:原服务器代码在握手完成后没有任何响应,直接阻塞在等待接收消息,导致浏览器无法确认握手成功,最终触发超时。修复后主动发送确认消息,客户端onopen会立即触发。
  2. ConnectionClosed异常:原代码未捕获客户端断开连接时抛出的异常,导致服务器输出报错回溯。添加try-except后可以优雅处理断开事件。
  3. Task was destroyed but it is pending:服务器关闭时,原代码中还有未完成的recv异步任务,修复后的代码在捕获断开异常后会自动清理任务,避免该警告。

环境配置建议

  • DMZ区域配置:将树莓派放入DMZ后,确保路由器已正确配置端口转发(将外网5678端口映射到树莓派的内网IP和5678端口),同时检查树莓派防火墙是否开放该端口:
    sudo ufw allow 5678
    
  • 网络连通性测试:先用telnet 86.205.245.32 5678测试端口是否能连通,如果不通,说明是网络层面的问题(端口未转发、防火墙拦截),如果能通再排查WebSocket逻辑。

内容的提问来源于stack exchange,提问作者Please help me

火山引擎 最新活动