Python在线聊天程序重连时出现ConnectionResetError[WinError10054]问题
Hey Rasmus, let's break down what's happening here and fix this connection issue.
问题根源
You’re absolutely right—old client connections aren’t being properly removed from the server’s clients dictionary. When you broadcast messages, the server still tries to send data to sockets that have already been closed (from the previous client session), triggering that ConnectionResetError.
This happens for two key reasons:
- If you close the client window directly (instead of typing
{stop}), the{stop}signal might not reach the server reliably (especially if there’s any network blip). - When a client disconnects unexpectedly, the server’s
client.recv(BUFSIZ)returns an empty byte string (b''), but your original code only checks for the{stop}message—so the thread gets stuck in the loop, leaving the dead socket in theclientsdict.
解决方案
We need to update the server code to handle all client disconnection scenarios, ensuring dead sockets are always cleaned up. Here’s the revised code with explanations:
Updated server.py Key Functions
def handle_client(client): name = "" try: name = client.recv(BUFSIZ).decode("utf8") if not name: # Handle case where client disconnects right after connecting return welcome = 'Welcome %s! Type {stop} to exit.' % name client.send(bytes(welcome, "utf8")) msg = "%s connected to the chat!" % name broadcast(bytes(msg, "utf8")) clients[client] = name while True: msg = client.recv(BUFSIZ) if not msg: # Empty bytes mean client disconnected unexpectedly break if msg != bytes("{stop}", "utf8"): broadcast(msg, name+": ") else: client.send(bytes("{stop}", "utf8")) break except Exception as e: print(f"Error with client {addresses.get(client)}: {e}") finally: # Guarantee cleanup no matter how the client exits client.close() if client in clients: del clients[client] if name: broadcast(bytes("%s left the chat." % name, "utf8")) def broadcast(msg, prefix=""): # Iterate over a copy of clients to avoid errors if dict changes mid-loop for sock in list(clients.keys()): try: sock.send(bytes(prefix, "utf8") + msg) except Exception as e: # Clean up any dead sockets encountered during broadcast print(f"Failed to send to {clients[sock]}: {e}") sock.close() del clients[sock]
Why This Works
finallyblock: Ensures socket cleanup runs whether the client exits normally, disconnects unexpectedly, or throws an error. No more leftover dead connections.- Check for empty
msg: Catches the standard TCP signal for a closed connection (whenrecv()returnsb''). - Broadcast with a dict copy: Prevents errors if we remove a client while broadcasting to others.
- Per-socket error handling in broadcast: If one client is dead, the rest still get messages—no more crashing the broadcast thread.
Quick Client-side Tweak
To make client exits more reliable, update the on_closing function to ensure the window closes properly:
def on_closing(event=None): try: my_msg.set("{stop}") send() finally: top.destroy()
With these changes, reconnecting clients will work smoothly, and the server won’t throw errors when old connections are left behind.
内容的提问来源于stack exchange,提问作者Rasmus A. C.

