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

客户端断开连接后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()

关键修改点说明

  1. 强制清理失效客户端:用finally块保证,不管客户端是正常退出还是异常断开,都会从clients列表里移除并关闭socket,不会留垃圾连接
  2. 封装广播函数:把广播逻辑抽出来,遍历列表的副本(list(clients)),避免遍历的时候列表被修改出问题;同时每个socket发送失败就直接清理,不让失效连接留在列表里
  3. 修复退出判断:现在先判断客户端发的原始消息是不是!exit,之前加了用户名根本判断不到
  4. 规范线程使用:换成threading.Thread,代码更易读,加daemon=True让主进程退出时线程自动结束
  5. 增强异常处理:连接收用户名的环节都加了异常处理,避免客户端刚连就断导致的问题
  6. 修正拼写错误:把recive改成了标准的receive

内容的提问来源于stack exchange,提问作者Marco Tomasi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 00:28:14