Python子线程执行socket.accept()时阻塞挂起无法接收连接
问题根因
你的代码里有几个直接导致accept()持续阻塞的问题,还有几个严重干扰排查的逻辑错误:
- 绑定地址错误:你将
HOST设置为具体静态IPv4地址的做法存在很高概率绑定失效。如果该地址没有直接配置在运行程序的本机网卡上(比如家用宽带的公网IP配在光猫/路由器上、云服务器公网IP映射在网关侧),即使bind()不报错,socket也无法接收到发往该地址的连接请求,accept()会永久等待。 - 日志位置错误造成误导:你把
"Client found, getting connection..."日志放在了s.listen()调用之后、s.accept()之前,完全不符合实际逻辑。s.listen()的作用只是将socket标记为被动监听模式、初始化连接等待队列,根本不会检测是否有客户端接入,这条日志打印时程序还没开始等待客户端连接,直接误导了你对阻塞位置的判断。 - 异常捕获逻辑吞掉有效信息:你用裸
except捕获所有异常,且没有打印具体异常栈,就算socket出现超时、被意外关闭、被系统回收等问题,你也只能拿到一句无意义的No clients found...日志,完全无法定位具体错误。 - 未正确配置socket超时:你在函数参数里传入了
socket_timeout但没有调用s.settimeout()给socket设置超时时间,accept()会进入永久阻塞模式,只要没有合法连接到达就会一直等待,这本身是阻塞socket的正常行为,不是线程挂死。 - 额外排查点:如果本地测试能连通但外部无法连接,大概率是系统防火墙、云服务器安全组、NAT端口映射没有放开对应端口的入站规则,连接请求被网络设备直接丢弃,根本到达不了你的服务进程。
修复后代码
Socket服务端逻辑
import socket import threading import logging from collections import deque # 监听本机所有网卡接口,无需填写具体IP HOST = '0.0.0.0' PORT = 10063 conn_queue = [] threads = deque() socket_timeout = 10 # 按需调整超时时间,单位秒 def get_connection(address=HOST, port=PORT, socket_timeout=socket_timeout, conn_queue=conn_queue): with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: # 开启端口复用,避免服务重启时出现端口占用报错 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind((address, port)) # listen参数指定未完成连接队列的最大长度 s.listen(5) logging.warning("Socket start listening on %s:%s", address, port) # 给socket设置超时,避免无连接时永久阻塞无任何日志输出 s.settimeout(socket_timeout) while True: try: conn, addr = s.accept() logging.warning("Get connection from %s, push to queue", addr) conn_queue.append({'conn': conn, 'address': addr}) except socket.timeout: # 超时为正常等待周期,打印调试日志即可,无需报错 logging.debug("No connection in %s seconds, continue listening", socket_timeout) continue except Exception as e: # 其他异常必须打印完整错误栈 logging.error("Accept connection failed: %s", str(e), exc_info=True) break
线程启动与主逻辑
注意:主线程如果执行完所有代码直接退出,作为守护线程的socket处理线程会被直接强制终止,必须保证主线程持续运行。
if __name__ == '__main__': socket_handler = threading.Thread( target=get_connection, daemon=True) socket_handler.start() threads.append(socket_handler) # 主线程保持运行,处理队列中的连接 while True: if conn_queue: conn_item = conn_queue.pop(0) conn = conn_item['conn'] addr = conn_item['address'] # 在这里写你的连接处理逻辑 conn.sendall(b"Connected to server successfully") conn.close()
验证步骤
- 替换HOST为
0.0.0.0重启服务,先在本机执行telnet 127.0.0.1 10063测试,如果能连通说明socket服务本身工作正常 - 本地连通后外部仍无法访问的,依次检查本机防火墙、云服务器安全组、路由器端口映射规则,放开10063端口的TCP入站权限
- 去掉
s.listen()后误导性的客户端已发现日志,所有连接相关日志放在accept()调用返回后打印
内容的提问来源于stack exchange,提问作者William Huynh
相关产品推荐
相关产品推荐

