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

Python多连接服务器场景下客户端Socket完成读写事件后无法关闭的问题

解决多连接Socket客户端无法自动关闭的问题

我帮你梳理下问题的核心原因,然后给出针对性的修复方案:

问题根源

你的客户端无法自动关闭主要来自两个关键问题:

1. 连接关闭的触发逻辑缺失

你修改后的客户端只有在服务器主动关闭连接(即recv_data为空)时才会执行关闭逻辑,但你的服务器在发送完所有消息后并不会主动断开——服务器的代码里,只有当客户端先关闭连接时,才会触发自身的关闭流程。这就形成了“互相等待”的死锁:客户端等服务器关,服务器等客户端关,两边都不会主动触发关闭。

另外,你在客户端的写事件里加了if data.outb and data.inb的判断,这会导致初始发送第一条消息时因为data.inb为空而发不出去,后续的消息收发逻辑也会混乱,这是完全没必要的限制。

2. 循环终止条件无法触发

因为客户端的连接一直没有被从选择器中注销(sel.unregister),所以sel.get_map()始终不为空,外层的break也就永远不会执行,程序会一直卡在循环里。


修复方案

我们需要调整客户端的逻辑,让它在发送完自身所有消息,并且接收完服务器的所有消息后主动关闭连接,同时修复发送逻辑的错误限制。

方案1:客户端主动判断收发完成(推荐)

这个方案不需要修改服务器代码,只调整客户端:

步骤1:在客户端初始化时记录服务器消息总长度

首先让客户端知道服务器要发送的消息总长度,这样才能判断是否收完所有内容:

def start_connections(host, port, num_conns):
    server_addr = (host, port)
    # 和你的server.py中的messages对应,计算服务器消息总长度
    server_msg_total = sum(len(m) for m in [b"Message 1 from server.", b"Message 2 from server."])
    for i in range(0, num_conns):
        connid = i + 1
        print("starting connection", connid, "to", server_addr)
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.setblocking(False)
        sock.connect_ex(server_addr)
        events = selectors.EVENT_READ | selectors.EVENT_WRITE
        data = types.SimpleNamespace(
            connid=connid,
            recv_total=0,
            server_msg_total=server_msg_total,  # 新增:记录服务器消息总长度
            messages=list(messages),
            outb=b"",
            inb=b"",
        )
        sel.register(sock, events, data=data)

步骤2:修复客户端的消息收发与关闭逻辑

移除写事件里的data.inb限制,同时添加“收发完成”的判断条件:

def service_connection(key, mask):
    sock = key.fileobj
    data = key.data
    if mask & selectors.EVENT_READ:
        recv_data = sock.recv(1024)
        print("received", repr(recv_data), "from connection", data.connid)
        if recv_data:
            data.recv_total += len(recv_data)
            data.inb += recv_data
            # 判断:收完服务器所有消息,且自身消息已发送完毕
            if data.recv_total == data.server_msg_total and not data.messages and not data.outb:
                print("closing connection", data.connid)
                sel.unregister(sock)
                sock.close()
        else:
            # 处理服务器主动关闭的情况
            print("closing connection", data.connid)
            sel.unregister(sock)
            sock.close()
    if mask & selectors.EVENT_WRITE:
        # 移除不必要的data.inb限制,恢复正常发送逻辑
        if not data.outb and data.messages:
            data.outb = data.messages.pop(0)
        if data.outb:
            print("sending", repr(data.outb), "to connection", data.connid)
            sent = sock.send(data.outb)
            data.outb = data.outb[sent:]
            # 发送完所有消息后,如果已经收完服务器的消息,直接关闭
            if not data.messages and not data.outb and data.recv_total == data.server_msg_total:
                print("closing connection", data.connid)
                sel.unregister(sock)
                sock.close()

步骤3:验证循环终止

当所有连接都被注销后,sel.get_map()会变为空,此时外层循环的if not sel.get_map(): break会触发,客户端程序会正常退出。


方案2:让服务器发送完消息后主动关闭(可选)

如果你不想在客户端硬编码服务器消息长度,可以修改服务器代码,让它在发送完所有消息后主动断开连接:

def service_connection(key, mask):
    sock = key.fileobj
    data = key.data
    if mask & selectors.EVENT_READ:
        recv_data = sock.recv(1024)
        if recv_data:
            data.inb += recv_data
            print("received", recv_data, "from", data.addr)
        else:
            print("closing connection to", data.addr)
            sel.unregister(sock)
            sock.close()
    if mask & selectors.EVENT_WRITE:
        if not data.outb and data.messages:
            data.outb = data.messages.pop(0)
        if data.outb:
            print("echoing", repr(data.outb), "to", data.addr)
            sent = sock.send(data.outb)
            data.outb = data.outb[sent:]
        # 新增:当所有消息发送完毕,主动关闭连接
        elif not data.messages and not data.outb:
            print("all messages sent, closing connection to", data.addr)
            sel.unregister(sock)
            sock.close()

这样服务器发送完所有消息后会主动断开,客户端收到recv_data为空的信号后就会关闭自身连接,同样能解决问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 09:22:35