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

Python Socket设置settimeout后recv仍不超时,服务器挂起问题求助

问题根源分析

你的服务器出现无限等待且CPU占用高的情况,核心是两个关键问题:

1. recvString函数未处理客户端断开场景

当客户端异常退出(TCP连接断开)时,socket.recv(1)会立即返回空字节串(b''),表示连接已关闭。但你的recvString函数仅判断了b == b'\x00'的终止条件,完全忽略了空字节串的情况,直接导致函数进入无限循环:

  • 每次recv(1)立刻返回空,不会触发超时(超时仅在recv阻塞等待数据时生效)
  • 循环无休无止,直接拉满CPU

2. finally块位置错误

当前finally块嵌套在while True的try语句内,意味着每次循环都会执行sock.close(),第一次处理完命令后socket就被关闭了,后续操作本应报错,但因为客户端已断开,反而加剧了上面的无限循环问题。

修复方案

修正recvString函数

添加对空字节串的判断,收到空字节串时立即终止循环:

def recvString(sock):
    buff = bytearray()
    while True:
        b = sock.recv(1)
        # 处理客户端断开的情况
        if not b:  # 空字节串表示连接已关闭
            break
        if b == b'\x00':
            break
        buff += b
    return buff.decode() if len(buff) else ''

调整finally块位置

将finally移到while True循环外部,确保会话结束后才关闭socket:

def session(sock, args):
    sess = threading.local()
    sess.sock = sock
    sess.__dict__.update(args)

    print("-" * 60)
    print("Session opened   from={addr}.".format(addr=sock.getpeername()))
    sock.settimeout(10.0)
    try:
        while True:
            try:
                print('about to wait on recv', sock.gettimeout())
                cmd = recvString(sock)
                print('got cmd =', cmd)
                # 若cmd为空,说明客户端已主动断开
                if not cmd:
                    print("Client disconnected")
                    break
                if   cmd.startswith('foo'):  doFoo(sess, cmd)
                elif cmd.startswith('bar'):  doBar(sess, cmd)
                elif cmd.startswith('baz'):  doBaz(sess, cmd)
                elif cmd.startswith('end'):  break
                else:
                    raise Exception("Protocol Error: bad command '{}'".format(cmd))
            except TimeoutError as err:
                print("Error protocol timeout")
                break
    finally:
        try:
            sock.close()
        except:
            pass
        print("Session closed.")
额外优化建议
  • 可以在recvString收到空字节串时抛出异常,让上层逻辑统一处理,代码结构更清晰
  • 避免每次recv仅读取1字节,这会增加系统调用次数降低性能,建议使用更大的缓冲区(比如recv(1024)),再拆分数据中的\x00分隔符

内容的提问来源于stack exchange,提问作者tim.mothery

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 17:25:19