Webman的WebSocket心跳需手动实现应用层机制,因其默认不启用心跳逻辑,onMessage收不到ping帧,onClose也不会因空闲超时触发;必须客户端每45秒发JSON心跳、服务端记录last_active_time并定时扫描超60秒未活动连接主动关闭,同时Nginx需配置proxy_read_timeout≥300秒以避免静默断连。

Webman 的 WebSocket 心跳不是开个配置就完事
Webman 默认不启用任何心跳逻辑,onMessage 收不到 ping 帧,onClose 也不会因空闲超时触发——它只做协议透传。你看到连接“突然断了”,大概率是 Nginx、CDN 或防火墙在背后静默关掉的,Webman 自己根本不知道。
必须手动实现应用层心跳,且客户端和服务端要对齐
别指望浏览器自动发 ping 帧(它不会),也别依赖 Webman 某个隐藏开关。真实可用的心跳得靠双方约定格式 + 定时触发:
- 客户端用
setInterval(() => ws.send(JSON.stringify({type: 'heartbeat'})), 45000),每 45 秒发一次 JSON 心跳 - 服务端在
onMessage里识别该消息,更新连接的last_active_time时间戳(存在$connection->last_active_time = time()) - 启动一个全局定时器(如
Webman\Timer::tick(50000, [...])),遍历所有连接,对time() - $conn->last_active_time > 60的调用$conn->close() - 务必在
onClose和onError中清理对应连接的时间戳,否则内存泄漏
Webman 配合 Nginx 时,proxy_read_timeout 是第一道生死线
Webman 自己设了 300 秒空闲检测没用,只要 Nginx 在 60 秒内没收到任何数据,它就直接 FIN 后端连接。这个行为不可绕过:
- Nginx 配置里必须显式加大
proxy_read_timeout 300(建议 ≥ 客户端心跳间隔 × 2) - 同时配
proxy_send_timeout 300,避免大消息阻塞后被误判 - 确认 Nginx 编译时启用了
--with-http_v2_module和--with-stream_ssl_module,否则 WebSocket 升级可能失败 - 如果走 CDN,先查清它是否支持 WebSocket;Cloudflare 要打开「WebSockets」开关,否则默认 100 秒强制断连
客户端 readyState 判断和重连策略漏掉一个,心跳就白加
前端发心跳前不检查状态,等于往已关闭的 socket 写数据,控制台报错但服务端完全无感知:
立即学习“PHP免费学习笔记(深入)”;
- 每次发心跳前加判断:
if (ws.readyState === WebSocket.OPEN) { ws.send(...) } - 监听
onclose后启动带退避的重连:setTimeout(connect, Math.min(1000 * Math.pow(2, retryCount), 30000)) - 记录每次心跳响应耗时,日志里打上
diff_ms = Date.now() - lastSentTime,断连排查时能快速区分是网络抖动还是服务卡死 - 别用单次超时就关连接——弱网下连续丢两个包很常见,应基于最近 3 次心跳时间戳综合判断是否失联
心跳机制不是加个定时器就稳了,它是一条链:客户端定时发 → Nginx 不拦截 → Webman 正确收 → 服务端及时记时间 → 定时器准确扫 → 异常连接真关闭 → 客户端能重连。任一环松动,长连接就在某个你看不见的地方悄悄断掉。



















