onClose未执行主因是连接异常断开(如掉电、NAT超时)致FIN包未送达,Workerman无法感知;高并发下死链堆积更易耗尽资源,须依赖应用层心跳(如每30秒检测lastReceiveTime超60秒则close)主动清理。

onClose 回调在高并发下“没执行”,大概率不是它被跳过了,而是连接根本没被正常断开 —— Workerman 无法感知,自然不会触发。
这背后是 TCP 协议层和应用层的错位:Workerman 只能靠对端发 FIN 包、或本端 close() 调用来判断连接结束。一旦硬件掉电、网线拔掉、NAT 超时静默回收连接,FIN 就永远不会来,onClose 就永远等不到。
为什么高并发时这个问题更明显?
并发量一大,连接数多,系统资源(如 TIME_WAIT 状态数、文件描述符)吃紧,NAT 设备/防火墙/运营商网关更容易提前丢弃“空闲但未 FIN”的连接;而 Workerman 还在等那个永远不会来的 FIN。
netstat 显示 ESTABLISHED,但客户端早已失联
- 用
netstat -anp | grep :端口查看,大量连接卡在ESTABLISHED,但客户端实际已断网或重启 - 这类“死链”会持续占用
TcpConnection实例、内存、fd,最终拖慢整个事件循环,甚至导致新连接拒绝 -
onClose不会触发,因为内核没通知,Workerman 无从得知
不依赖 FIN 的主动探测方案
必须自己做心跳,不能指望 TCP keepalive(默认 2 小时才探活,太迟):
- 在
$worker->onMessage里记录每个$connection->lastReceiveTime = time() - 用
Worker::addTimer(10, function() { ... })每 10 秒遍历所有连接,对超过 60 秒没收到数据的调用$connection->close() - 若用 GatewayWorker,直接配
Gateway::$pingNotResponseLimit = 60和Gateway::$pingInterval = 30,它会自动发ping并清理无响应连接 - 注意:不要在定时器里做耗时操作(如查 DB、写文件),否则阻塞事件循环,反而让心跳检测更不准
UDP 场景下 onClose 根本不生效
如果协议走的是 UDP(比如 udp://0.0.0.0:8080),onClose 回调压根不会注册,也不会触发 —— UDP 是无连接的,没有“断开”概念。此时只能靠业务层超时机制(如收到某 ID 数据后 30 秒没续命,就视为离线)。

















