workerman tcp长连接频繁断开主因是中间设备空闲超时静默关闭,需自行实现双向心跳(客户端≤25秒发ping、服务端45秒检测+60秒清理),禁用nagle算法,并排查worker进程健康、监听地址及网络通路。

Workerman 5.2.3 连接频繁断开,多数情况不是框架崩溃或代码异常,而是连接在中间网络设备上被静默关闭,服务端和客户端都“感知不到断开”,表现为发包无响应、onClose不触发、日志无报错。排查需分三层:网络层(是否真连上了)、协议层(心跳是否有效)、进程层(Worker 是否健康运行)。
检查连接是否被中间设备静默关闭
这是最常见原因。NAT网关、云厂商SLB、运营商防火墙普遍设置空闲超时(30–90秒),一旦连接无数据流动,它们会直接丢弃后续包或发RST,但不通知Workerman。此时socket仍显示ESTABLISHED,onClose自然不触发。
- 在服务端执行
ss -i src :你的端口,观察retrans(重传)次数是否持续上升——上升说明SYN/ACK或数据包被拦截 - 用
tcpdump -i any port 你的端口抓包:若客户端已发数据,服务端收不到回包,基本可确认是中间链路切断 - 纯TCP场景下,
$worker->pingInterval和$worker->pingNotResponseClose完全无效(仅WebSocket生效),必须自行实现双向心跳
验证并优化心跳机制
心跳不是可选项,而是TCP长连接存活的必要保障。关键在于“双向”和“区分业务与心跳”。
- 客户端应 ≤25 秒发送一次
{"type":"ping"}(留足余量,避开60秒超时陷阱) - 服务端需单独维护
$connection->lastMessageTime:仅在收到非心跳消息时更新;收到ping只回pong,不更新时间戳 - 另起定时器(如每45秒执行),遍历所有连接,对
time() - $connection->lastMessageTime > 60的连接主动调用$connection->close() - 客户端必须实现超时重连逻辑:发 ping 后 3 秒内未收到 pong,立即断开并重连,否则服务端清理了,客户端还在傻等
确认Worker进程是否稳定运行
连接断开也可能是Worker子进程意外退出,而非网络问题。状态码 256 是典型信号。
- 执行
ps aux | grep WorkerMan:必须同时看到master process和至少一个worker process;只有 master 没有 worker,大概率是onWorkerStart抛了未捕获异常 - 查
workerman.log最末尾,找exit with status 256记录——这表示PHP子进程被内核强制终止(如禁用了pcntl_fork或posix_kill) - 运行
php -i | grep disable_functions,重点检查pcntl_fork、posix_kill、posix_getpid是否被禁用 - 在
start.php开头加三行:error_reporting(E_ALL);ini_set('display_errors', 'on');use Workerman\Config; Config::$debug = true;
验证监听地址与客户端连通路径
看似连得上,其实根本没走对路。本地 telnet 127.0.0.1 端口 成功 ≠ 外部能连。
- 执行
ss -tuln | grep :端口:必须看到*:端口或0.0.0.0:端口;若只显示127.0.0.1:端口,说明绑定的是回环地址,外部请求进不来 - 从真实客户端机器执行
telnet 你的服务器公网IP 端口—— 这步不可跳过 - 云服务器务必检查安全组(入方向放行TCP端口);本地开发用Docker/WSL/VirtualBox时,宿主机防火墙常默认拦截,需临时关闭或添加例外
- 若使用Nginx反向代理WebSocket,确认配置含三行:
proxy_http_version 1.1;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection "upgrade";











