TP8.0 WebSocket断线重连失效主因是服务端未显式处理ping/pong心跳、心跳间隔错位、重连逻辑缺陷及中间件误拦截;需手动注册onMessage响应、对齐ping_interval、优化重连策略并禁用HTTP中间件。

TP5 升级到 ThinkPHP 8.0 后 WebSocket 断线重连失效,不是框架“不支持”,而是 TP8.0 默认不处理应用层心跳,且服务端 onMessage 回调未显式注册或逻辑缺失——客户端发了 ping,服务端没回 pong,超时自然断开。
onMessage 必须注册且显式响应 ping/pong
TP8.0 的 think-swoole 或原生 Swoole WebSocket Server 不会自动识别 "ping" 字符串或 JSON 消息。你必须在服务端 WebSocket 事件注册中手动监听并响应:
- 检查
app/event/SocketEvent.php(或你自定义的事件类)是否在onMessage方法中做了if ($data === 'ping' || (is_string($data) && json_decode($data, true)['type'] === 'ping'))判断 - 确认返回的是严格匹配的
pong:用$frame->fd调用$server->push($frame->fd, 'pong')或$server->push($frame->fd, json_encode(['type' => 'pong'])) - 若用 JSON 格式,客户端和服务端字段名、大小写、嵌套层级必须完全一致;
json_decode($data, true)失败时不能静默跳过,应记录日志并忽略该帧
心跳间隔与服务端 ping_interval 必须对齐
客户端设 30s 发一次 ping,但 Swoole 配置里 ping_interval=55,会导致客户端先触发超时关闭连接——这不是网络问题,是节奏错位。
- 在
config/swoole.php中检查'websocket' => ['ping_interval' => 25],建议设为比客户端心跳间隔小 5–10 秒(如客户端 30s,则服务端设 25s) - 同时配
'ping_timeout' => 10,确保服务端在 10 秒内未收到 pong 就主动断连,避免僵尸连接堆积 - 客户端 JS 里不要写死
setInterval(() => ws.send('ping'), 30000),改用setTimeout链式调用,避免定时器叠加
重连逻辑必须防雪崩+判手动关闭
TP8.0 下常见重连失败,是因为旧连接未释放完就建新连接,或重连定时器没清理干净,导致并发建连压垮服务端。
- 在
onclose前加标记:this.isManualClose = false; // 默认非手动,用户点击“断开”时设为true,防止自动重连干扰 - 重连前清空旧定时器:
clearTimeout(this.pingTimer); this.pingTimer = null;,再新建 WebSocket 实例 - 重连延迟必须指数退避:
reconnectDelay = Math.min(30000, 3000 * Math.pow(2, attempt)),避免第 5 次重连直接打满 48s - 页面不可见时暂停重连:
if (document.hidden) return;,防止后台标签页持续建连
TP8.0 特有拦截点:中间件和路由可能吞掉消息
TP8.0 的中间件链默认不处理 WebSocket 消息,但如果在 app/middleware.php 中误将 WebSocket 路由也套用了 HTTP 中间件(比如 JSON 解析中间件),就会在 onMessage 触发前就把原始字符串当 HTTP body 解析失败,静默丢弃。
- 确认 WebSocket 路由未被 HTTP 中间件包裹:检查
route/websocket.php是否独立注册,且中间件数组为空或仅含自定义 WS 中间件 - 禁用所有对 WebSocket 连接生效的 JSON 解析类中间件;
onMessage收到的$data是原始字符串,解析必须在业务层做 - 在
onMessage开头加Log::info('ws recv', ['raw' => $data]);,确认消息是否真正抵达——如果日志没出现,说明被前置逻辑截断了
最易被忽略的是:TP8.0 下 WebSocket 连接对象生命周期和协程调度强耦合,$server->push() 调用失败不会抛异常,只返回 false,而很多示例代码没做返回值判断。务必检查每次 push 结果,false 时记录 fd 和错误码,否则你以为回了 pong,其实根本没发出去。

















