必须在onConnect中初始化$connection->lastMessageTime=time(),onMessage开头立即刷新该时间戳,再通过55秒定时器遍历connections比对$now-$conn->lastMessageTime>60时调用close清理超时连接。

Workerman服务运行中,客户端因NAT超时、网络抖动或异常断开后,连接对象仍滞留在$worker->connections中,导致内存持续增长、误判在线状态、资源泄漏。必须在onConnect和onMessage中记录活跃时间,并用定时器精准识别并关闭超时连接。
初始化连接活跃时间戳
在客户端建立连接的瞬间,为每个新连接绑定一个lastMessageTime属性,作为心跳检测的起点。
进入onConnect回调函数,直接为$connection赋值:【$connection->lastMessageTime = time();】
这一步不能省略或延迟到onMessage中执行——如果客户端连接后不发任何数据(比如刚连上就断网),没有这个初始时间戳,定时器将无法判断该连接是否已“静默死亡”。
更新通信活跃时间
每当收到客户端消息,立即刷新该连接的最后通信时间。
在onMessage回调中写入:【$connection->lastMessageTime = time();】
注意:必须放在onMessage最开头,且不能加任何条件判断。即使收到的是空字符串、非法JSON或心跳包以外的垃圾数据,也要更新时间戳——只要TCP数据到达,就说明链路暂时通畅。
启动定时器扫描并清理超时连接
使用Worker::addTimer(或Timer::add)设置周期性任务,遍历所有连接,比对当前时间和lastMessageTime差值。
第一步:在onWorkerStart中添加定时器,间隔设为55秒(必须严格小于超时阈值):
Timer::add(55, function () use ($worker) {
第二步:获取当前时间戳 $now = time();
第三步:遍历$worker->connections中每一个$conn:
④ 判断 if ($now - $conn->lastMessageTime > 60) { $conn->close('idle timeout'); }
这里60秒是服务端允许的最大空闲时长,55秒定时频率提供了5秒安全缓冲——避免因定时器执行延迟导致误杀刚活跃过的连接。
可选:支持客户端主动心跳请求
当客户端发送标准心跳包(如{"type":"ping"})时,服务端应立即响应pong,不参与耗时逻辑。
方法一:在onMessage中增加类型判断
if (is_string($data) && strpos($data, '"type":"ping"') !== false) { $connection->send('{"type":"pong"}'); return; }
方法二:用json_decode快速解析(仅当确定数据为合法JSON时启用)
$msg = json_decode($data, true); if (isset($msg['type']) && $msg['type'] === 'ping') { $connection->send(json_encode(['type' => 'pong'])); return; }
注意:不要在心跳响应里调用数据库查询或sleep等阻塞操作,否则会拖慢整个事件循环。

















