Docker中Python TCP对接PLC偶发Errno 99无法分配请求地址问题求助
问题成因
- 核心原因是本地临时端口耗尽:你当前的代码逻辑每次发送数据都会新建一个TCP连接,发送完成后主动调用
close()关闭连接,主动关闭连接的一方会进入TIME_WAIT状态,默认会持续2MSL(通常为1-2分钟),该状态下对应的本地端口会被占用,无法分配给新连接。按照你每小时300次连接的运行频率,长期运行后端口释放速度跟不上新建速度,就会抛出[Errno 99] Cannot assign requested address错误。 - 代码缺少异常兜底逻辑:如果
connect()、send()执行过程中抛出异常,当前代码不会执行到socket.close(),会直接导致socket资源泄漏,进一步加快端口耗尽的速度。 - 由于Docker使用了
--network host参数,容器和宿主机共享网络栈,端口耗尽问题会直接作用在宿主机网络层,重启设备/插拔网线会重置所有网络连接,释放占用的端口,因此可以临时恢复业务。
解决方案
最优方案:改用TCP长连接复用
从根源上避免频繁新建/关闭连接产生的大量TIME_WAIT,初始化一次连接后重复使用,连接断开后自动重连即可,参考修改逻辑:
# 全局或类成员变量保存socket实例 socket = None def init_connection(): global socket try: if socket: socket.close() socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) socket.setsockopt(socket.SOL_SOCKET, 25, str("enp6s0" + '\0').encode('utf-8')) socket.connect(('192.168.1.123', 2000)) except Exception as e: socket = None # 可根据需要添加重试逻辑 # 发送数据逻辑 if some_boolean: if not socket: init_connection() try: sent1 = socket.send(lane_id.to_bytes(2, 'little')) sent2 = socket.send(delay.to_bytes(2, 'little')) sent3 = socket.send(open_lane.to_bytes(2, 'little')) total_bytes = sent1 + sent2 + sent3 except Exception as e: # 连接出错重置,下次发送自动重连 socket = None
临时方案:优化短连接逻辑+调整内核参数
如果业务必须保留短连接逻辑,可做两项优化:
- 新增try-finally结构,确保无论请求是否成功,socket都会被关闭:
socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: socket.setsockopt(socket.SOL_SOCKET, 25, str("enp6s0" + '\0').encode('utf-8')) socket.connect((TCP_IP_Client, TCP_PORT_Client)) if some_boolean: sent1 = socket.send(lane_id.to_bytes(2, 'little')) sent2 = socket.send(delay.to_bytes(2, 'little')) sent3 = socket.send(open_lane.to_bytes(2, 'little')) total_bytes = sent1 + sent2 + sent3 finally: socket.close()
- 修改宿主机
/etc/sysctl.conf配置,加速TIME_WAIT回收、扩大可用端口范围:
# 开启TIME_WAIT套接字快速回收 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_tw_recycle = 1 # 扩大本地临时端口范围 net.ipv4.ip_local_port_range = 1024 65535
修改完成后执行sysctl -p即可生效。
内容的提问来源于stack exchange,提问作者Tim Treurniet
相关产品推荐
相关产品推荐

