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
相关产品推荐
相关产品推荐

