应手动响应 ping 帧或发自定义心跳消息;Swoole 需注册 onPing 回调,Workerman 需在 onMessage 中识别 opcode 0x9 并发送最小 pong 帧;浏览器不自动发 ping,需前端定时 send 自定义心跳消息。

WebSocket 心跳包该用 ping 还是 send 自定义消息?
PHP 的 WebSocket 服务端(如 Swoole、Workerman)不支持浏览器那种自动 ping/pong 帧——浏览器发 ping,服务端必须显式回复 pong,否则连接可能被中间代理(如 Nginx、CDN)静默断开。但 PHP 扩展本身不自动处理 ping 帧,得手动监听并响应。
- Swoole 中需注册
onPing回调,否则收到ping不会自动回pong,连接大概率在 60 秒后被 Nginx 关闭 - Workerman 没有内置
onPing,得靠onMessage拦截二进制帧,判断 opcode 是否为0x9(ping)再手动$connection->send("\x8a\x00")(最小 pong 帧) - 浏览器不会主动发
ping,得前端定时ws.send(JSON.stringify({type:"heartbeat"})),服务端按业务逻辑回复确认,更可控
Swoole WebSocket 心跳超时时间怎么配才不被 Nginx 断连?
Nginx 默认 proxy_read_timeout 是 60 秒,只要这期间没收到任何数据(包括 ping/pong),就会关闭后端连接。Swoole 的 heartbeat_check_interval 和 heartbeat_idle_time 只管内存里连接状态,不管反向代理。
- 必须同步调大 Nginx 配置:
proxy_read_timeout 300(建议 ≥ 客户端心跳间隔 × 2) - Swoole 启动时设
'websocket.heartbeat_idle_time' => 240,'websocket.heartbeat_check_interval' => 60,避免自己先踢人 - 若用 SSL + CDN,还要确认 CDN 是否支持 WebSocket 长连,有些免费 CDN 会强制 120 秒断连,换用 Cloudflare 的「WebSockets」开关打开的方案
客户端发心跳失败,服务端怎么区分是网络断了还是只是卡顿?
只依赖单次心跳超时就关连接,容易误杀。真实场景中,弱网下可能连续丢几个包,但连接其实还活着。
- 服务端不要一收不到心跳就
$connection->close(),应记录最近 3 次心跳时间戳,仅当「最后成功心跳距今 youjiankuohaophpcn 3 × 心跳间隔」才判定失效 - 客户端发心跳前先检查
ws.readyState === 1,避免往已关闭连接写数据报InvalidStateError - 在
onClose回调里打日志,带上$connection->lastMessageTime和当前时间差,方便事后判断是超时还是主动断开
心跳机制不是“加个定时器就稳了”,关键在服务端响应及时性、反向代理配置一致性、以及客户端和服务端对“失联”的定义是否对齐。漏掉任意一环,长连接都会在某个环节悄无声息地断掉。
立即学习“PHP免费学习笔记(深入)”;











