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>
错误原因与环境配置建议
错误原因分析
- 握手超时:原服务器代码在握手完成后没有任何响应,直接阻塞在等待接收消息,导致浏览器无法确认握手成功,最终触发超时。修复后主动发送确认消息,客户端
onopen会立即触发。 - ConnectionClosed异常:原代码未捕获客户端断开连接时抛出的异常,导致服务器输出报错回溯。添加
try-except后可以优雅处理断开事件。 - 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




