workerman的onclose回调仅在正常断开时触发,中间设备静默断开(如nat超时)不会触发;必须配合双向心跳机制——客户端≤25秒发ping、服务端45秒检测+60秒清理,才能确保onclose可靠执行并完成资源清理。

Workerman 5.2.3 中,客户端连接断开事件由 $worker->onClose 回调统一处理,但必须注意:该回调仅在连接被正常关闭(如客户端主动 close、TCP FIN/RST 到达)时触发;若连接被中间设备静默断开(如 NAT 超时、防火墙丢包),onClose 不会执行——这是最常被误解的点。
onClose 的正确使用方式
- 它是连接生命周期的终点钩子,用于清理资源、通知其他模块、更新用户状态等
- 必须配合连接保活机制(如心跳)才能覆盖“假死”场景,不能单独依赖它感知所有断连
$worker->onClose = function ($connection) {
// 1. 清理该连接绑定的自定义数据(如用户ID、房间号)
if (isset($connection->uid)) {
unset($_SESSION['online_users'][$connection->uid]);
}
// 2. 通知其他在线用户(如广播“xxx已下线”)
foreach (Worker::$connections as $conn) {
if ($conn !== $connection && isset($conn->room) && $conn->room === $connection->room) {
$conn->send(json_encode(['type' => 'offline', 'uid' => $connection->uid]));
}
}
// 3. 记录日志(建议用 file_put_contents 或 Monolog,避免阻塞)
file_put_contents('/tmp/close.log', date('Y-m-d H:i:s') . " uid={$connection->uid} closed\n", FILE_APPEND);
};
为什么有时 onClose 看似“不触发”?
- 客户端网络中断(如切WiFi、4G信号丢失),未发送 FIN,服务端 socket 仍为 ESTABLISHED
- 运营商/NAT/云SLB 设置空闲超时(常见 60–180 秒),超时后直接丢弃后续包或发 RST,但不通知 Workerman
- 此时
ss -i src :端口可见retrans持续增长,tcpdump显示客户端发包无回包
必须搭配心跳机制补全断连检测
纯 TCP 场景下,$worker->pingInterval 等 WebSocket 参数完全无效。需自行实现双向心跳:
-
客户端:每 ≤25 秒发
{"type":"ping"},等待pong,3 秒未收到即断开重连 -
服务端:
- 在
onConnect初始化:$connection->lastMessageTime = time(); - 在
onMessage中:仅当非心跳消息才更新时间戳 - 启动定时器(如每 45 秒)遍历连接,对
time() - $connection->lastMessageTime > 60的连接调用$connection->close()
- 在
这样,onClose 才能被真正触发,并完成后续清理。
不复杂但容易忽略











