Python socket服务空闲一段时间后停止接受连接问题排查
问题根因与修复方案
你的服务出现空闲数分钟后无法接收新连接的问题,90%以上概率是以下几个代码/配置问题导致的,按出现概率从高到低排列:
- 监听地址绑定错误
你当前代码将socket绑定到socket.gethostname()解析出的地址,这个地址通常是本机网卡的动态内网IP,并非通配监听地址。当系统出现DHCP租期刷新、网卡短暂休眠重置、网络闪断的情况(这类事件通常在系统空闲数分钟后触发),原IP和网卡的绑定关系失效,监听socket会直接进入僵死状态,accept()调用永久阻塞,再也接收不到新连接。
修复方式:将绑定地址改为'0.0.0.0',监听所有网卡的入站请求,对应代码改为self.ssFT.bind(('0.0.0.0', 51002))。 - 已连接套接字资源泄漏
你贴出的代码省略了accept()获取到客户端连接conn后的处理逻辑,如果这部分逻辑没有在连接生命周期结束时(无论正常处理完成、还是读写报错、超时)显式调用conn.close()释放连接,所有建立过的连接会持续占用系统文件描述符。加上你设置的listen连接队列长度只有10,队列很快会被半开连接、未释放的旧连接占满,新连接无法进入队列,服务就会停止响应。
修复方式:所有客户端连接的处理逻辑必须包裹try/finally块,在finally段固定执行conn.close();同时将listen()的队列长度参数调大到128,降低连接队列被打满的概率。 - 阻塞socket无超时导致假死
当前监听socket为默认永久阻塞模式,没有设置超时时间。一旦底层网络栈出现异常导致socket不可读,accept()会永远卡住,既不会抛出错误,也不会循环检查self.c.kill_all_threads标志位,服务直接假死。
修复方式:给监听socket设置合理超时,比如self.ssFT.settimeout(30),在主循环中捕获socket.timeout异常后直接进入下一轮循环,既可以避免永久假死,也能保证退出标志位被正常响应。 - 异常捕获逻辑吞掉关键报错
你当前bind阶段用了裸except:捕获所有异常,只打印一句绑定失败重启,完全看不到具体错误原因(比如端口占用、权限不足、资源不足等),会大幅拉长排查时间。
修复方式:捕获异常时把具体错误信息打印到日志里,方便定位偶发问题。 - Windows平台网卡节能配置干扰(如果服务运行在Windows系统)
从你代码里替换路径反斜杠的逻辑判断,你大概率在Windows上部署服务。Windows默认开启网卡节能选项,空闲数分钟后会自动关闭网卡电源节约电量,绑定在网卡上的socket会直接失效。
修复方式:打开设备管理器,找到对应使用的网卡,在属性-电源管理里取消勾选「允许计算机关闭此设备以节约电源」。
修正后的核心代码参考
import os import socket class MyClass: ... def socket_server(self): cwd = os.getcwd().replace("\\","/") + "/" BUFFER_SIZE = 1024 try: self.ssFT = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.ssFT.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 绑定全网卡地址,设置30秒超时避免永久阻塞 self.ssFT.bind(('0.0.0.0', 51002)) self.ssFT.settimeout(30) self.c.util.fprint('listening on 0.0.0.0:51002') # 调大连接等待队列长度 self.ssFT.listen(128) except Exception as e: self.c.kill_all_threads = True self.c.util.fprint(f'Socket bind failed, err: {str(e)} - restarting') while self.c.kill_all_threads == False: conn = None try: conn, address = self.ssFT.accept() print(conn, address) # 此处编写你的业务处理逻辑 except socket.timeout: # 监听超时后直接进入下一轮循环,检查退出标志 continue except Exception as e: self.c.util.fprint(f'Handle connection failed, err: {str(e)}') finally: # 无论处理成功还是失败,都要关闭连接释放资源 if conn: conn.close() ...
内容的提问来源于stack exchange,提问作者user17357
相关产品推荐
相关产品推荐

