PHP原生socket无法维持WebSocket长连接,因其阻塞式设计不处理帧解析、心跳保活及空闲超时,易被Nginx(60秒)、云LB(30秒)或NAT网关强制断连;必须依赖Swoole/Workerman等异步框架,配合服务端心跳探测(heartbeat_check_interval/heartbeat_idle_time)、客户端定时PING/PONG、中间件超时对齐及自动重连四层机制方可稳定运行。

PHP本身不支持原生长连接,stream_socket_client 建连后不加干预,30–120 秒内必断——这不是你的代码问题,是协议层、中间件和 PHP 运行模型共同决定的。必须用异步框架或手动轮询+心跳+重连组合拳来扛。
为什么 PHP 原生 socket 无法维持 WebSocket 长连接
PHP CLI 模式下用 fsockopen 或 stream_socket_client 手动实现 WebSocket 客户端,本质是阻塞式 TCP 连接。它既不自动解析帧,也不处理 PING/PONG 控制帧,更不会在空闲时发心跳。结果就是:
- Nginx 默认
proxy_read_timeout 60,60 秒无数据直接 FIN 断连(且不通知后端) - 云厂商 LB(如阿里云 SLB)默认空闲超时仅 30 秒,比 Nginx 更激进
- 运营商 NAT 网关、家用路由器普遍在 60–90 秒静默回收空闲连接
-
ignore_user_abort(true)+sleep()是伪长连接,进程卡死、内存泄漏、无法响应中断信号
Swoole 中启用 heartbeat_check_interval 和 heartbeat_idle_time
Swoole 是目前 PHP 生态最稳的 WebSocket 解决方案,它的 heartbeat_check_interval 不是“发心跳”,而是“检查心跳”——服务端主动探测客户端是否还活着。关键参数必须配对使用:
-
'heartbeat_check_interval' => 30:每 30 秒扫描一次所有连接 -
'heartbeat_idle_time' => 60:若某连接最后收包时间距今 > 60 秒,立即 close - 这两个值要满足:
heartbeat_idle_time ≥ 心跳间隔 × 2,否则容易误杀正常连接 - 客户端也得同步发
PING(例如每 25 秒一次),否则服务端扫描时发现超时就断
示例配置:
立即学习“PHP免费学习笔记(深入)”;
$server = new Swoole\WebSocket\Server("0.0.0.0", 9501);
$server->set([
'heartbeat_check_interval' => 30,
'heartbeat_idle_time' => 60,
]);
Workerman 客户端保活必须自己写心跳定时器
Workerman 没有内置服务端心跳检测机制(不像 Swoole),所以你得在 onConnect 后手动启动一个定时器,否则连接照样被中间设备掐断:
- 用
$connection->tick(25000, function() { ... })每 25 秒发一次PING帧(\x89\x00) - 不能只发不收:必须监听
onMessage,收到PONG帧时更新该连接的活跃时间戳 - 配合后台任务每 10 秒遍历所有连接,对
time() - last_pong_time > 90的连接主动close() - 别依赖
onClose回调——网络闪断时这个回调根本不会触发
Workerman 服务端没有 last_ping_time 这种现成字段,得你自己往 $connection 上挂属性:
$connection->last_pong_time = time();
PHP 客户端用 pcntl_fork + stream_select 实现轻量守护(仅限 Linux)
如果你非要用纯 PHP 写客户端(比如对接第三方 WebSocket API),又不想装扩展,唯一可行路径是 pcntl_fork + stream_select 轮询。但要注意几个硬限制:
- 必须编译 PHP 时启用
pcntl和posix扩展,Windows 直接不可用 -
stream_select第四个参数是超时秒数,第五个是微秒数;设成0, 500000表示每次最多等 500ms,避免死锁 - 收到数据后必须手动解析 WebSocket 帧(包括掩码解密、FIN/opcode 判断),不能直接
fread -
PING帧必须是二进制:先发\x89\x00,服务端回\x8a\x00;不能发字符串"PING" - 必须捕获
SIGTERM并优雅关闭 socket,否则kill -9后残留连接会堆积
真正稳定的 PHP WebSocket 长连接,从来不是靠“连上就完事”,而是靠服务端心跳探测 + 客户端主动报活 + 中间件超时对齐 + 断连自动恢复这四层兜底。少一层,都可能在凌晨三点掉线。



















