客户端断开连接后Python Socket聊天服务器异常停止工作的技术求助
问题排查与修复方案
嘿,你的聊天服务器遇到的问题其实很典型——客户端断开后,你没把失效的socket从全局的clients列表里删掉。后面新客户端发消息时,服务器广播会尝试给已经失效的socket发数据,直接触发异常把当前线程搞崩,所以新客户端一说话就被断开。
我帮你拆解下具体的问题点:
- 不管客户端是正常打
!exit退出,还是突然断网,你的代码只退出了接收循环,但clients列表里还留着那个没用的socket - 广播的时候遍历所有clients,碰到失效的socket就会抛异常,当前处理新客户端的线程直接挂掉,客户端自然就被断开了
- 另外还有俩小bug:
recive拼写错了(应该是receive),还有!exit的判断完全没用——你把用户名加在消息前面了,永远不可能等于!exit对吧?
修复后的完整代码
import socket from threading import Thread import time # 存储客户端连接的全局列表 clients = [] def receive(client): global clients try: # 接收客户端名称 name = client.recv(2048).decode("utf-8") print(f"{name} 已加入聊天") except: # 如果连名称都没收到就断开,直接清理 clients.remove(client) client.close() print("连接异常断开") return try: while True: # 接收客户端消息 string = client.recv(2048).decode("utf-8") # 处理客户端退出命令——先判断原始消息,别加用户名 if string.strip() == "!exit": broadcast_message(f"系统通知: {name} 已退出聊天", exclude=client) break # 组装消息并广播 full_message = f"{name}: {string}" broadcast_message(full_message, exclude=client) print(full_message) except: # 客户端异常断开(比如突然断网) broadcast_message(f"系统通知: {name} 意外断开连接", exclude=client) finally: # 无论正常还是异常退出,都必须清理这个客户端 if client in clients: clients.remove(client) client.close() print(f"{name} 的连接已关闭") def broadcast_message(message, exclude=None): """把广播逻辑单独抽出来,更清晰,还能处理发送异常""" global clients # 遍历列表的副本,避免遍历过程中列表被修改(比如移除失效客户端)导致的问题 for c in list(clients): if c == exclude: continue # 不给发送消息的客户端自己发 try: c.send(bytes(message, "utf-8")) except: # 发送失败说明这个客户端已经挂了,直接删掉 clients.remove(c) c.close() if __name__ == "__main__": hostname = socket.gethostname() local_ip = socket.gethostbyname(hostname) port = 55555 server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind((local_ip, port)) server.listen() print(f"Server listening on {local_ip}:{port}") while True: client, address = server.accept() print(f"已建立连接 - {address[0]}:{address[1]}") clients.append(client) # 用Thread类代替_thread,更规范,daemon=True让线程随主进程一起退出 Thread(target=receive, args=(client,), daemon=True).start()
关键修改点说明
- 强制清理失效客户端:用
finally块保证,不管客户端是正常退出还是异常断开,都会从clients列表里移除并关闭socket,不会留垃圾连接 - 封装广播函数:把广播逻辑抽出来,遍历列表的副本(
list(clients)),避免遍历的时候列表被修改出问题;同时每个socket发送失败就直接清理,不让失效连接留在列表里 - 修复退出判断:现在先判断客户端发的原始消息是不是
!exit,之前加了用户名根本判断不到 - 规范线程使用:换成
threading.Thread,代码更易读,加daemon=True让主进程退出时线程自动结束 - 增强异常处理:连接收用户名的环节都加了异常处理,避免客户端刚连就断导致的问题
- 修正拼写错误:把
recive改成了标准的receive
内容的提问来源于stack exchange,提问作者Marco Tomasi
相关产品推荐
相关产品推荐

