要在ThinkPHP 6.0中成功运行WebSocket聊天室,必须确保Swoole环境启用协程与WebSocket、正确配置swoole.php启用WebSocket模块并指定合法handler和route_file、实现WsHandler的onOpen/onMessage/onClose逻辑、定义websocket.php路由映射、每个事件类实现__invoke方法调用push或broadcast,最后通过php think swoole启动并验证101握手及心跳保活。

要在 ThinkPHP 6.0 中跑起一个可用的 WebSocket 聊天室,必须让服务端能稳定握手、正确解析消息、广播不丢人、断开不残留——缺一不可。光配个配置文件或写个空事件类,浏览器连上就断,发消息没回音,上线人数永远显示 1,这类问题在真实部署中高频出现。
确认 Swoole 环境与扩展已就位
执行 php --ri swoole,检查输出里是否同时存在 websocket => enabled 和 coroutine => enabled。若只有前者,说明协程未启用,后续 onMessage 处理并发消息时会阻塞;若两者皆无,【必须先安装 Swoole 扩展并重启 PHP 服务】,否则所有后续配置全是空转。
运行 composer require topthink/think-swoole:^4.0,注意版本号不能省略,v3.x 不兼容 TP6.0 的事件处理器契约。
配置 swoole.php 启用 WebSocket 模块
打开 config/swoole.php,确保整个 'websocket' 配置是数组结构而非布尔值:
立即学习“PHP免费学习笔记(深入)”;
'websocket' => ['enabled' => true] —— 写成 'websocket' => true 会导致握手失败,客户端收不到 101 Switching Protocols 响应。
'handler' => \app\listener\WsHandler::class —— 类路径必须带命名空间且类文件真实存在,Swoole 启动时不会报错,但连接一建立就静默关闭。
'route_file' => app_path() . 'websocket.php' —— 这个路径要手动创建,文件可以为空,但不能不存在,否则服务启动时报 “Route file not found” 并退出。
创建并实现 WsHandler 事件处理器
第一步:执行命令生成监听器类php think make:listener WsHandler
第二步:编辑 app/listener/WsHandler.php,继承 \think\swoole\WebSocket,并补全三个核心方法:
onOpen 方法中,必须调用 $this->server->push($fd, json_encode(['type'=>'welcome','msg'=>'已连接'])); 主动发欢迎消息,否则前端常因收不到首帧而误判连接失败。
onMessage 方法里,先用 $data = json_decode($frame->data, true) ?: ['type'=>'raw', 'content'=>$frame->data]; 兜底解析,避免客户端发非 JSON 字符串导致整个连接崩溃;再根据 $data['type'] 分发逻辑,比如 chat 就调用 $this->broadcast(...),ping 就直接 $this->send($fd, json_encode(['type'=>'pong']))。
onClose 方法中,务必清理内存状态,例如 unset($this->onlineUsers[$fd]);,否则同一用户反复刷新页面,$fd 复用后会把旧状态覆盖掉,造成“一人上线,两人在线”的假象。
定义 websocket.php 消息路由映射
在 app/websocket.php(路径由 config/swoole.php 中 route_file 指定)中写入:
<?phpreturn [ 'chat' => \app\event\ChatEvent::class, 'join' => \app\event\JoinEvent::class,];
这个文件不是可选的——只要客户端发的是 {"type":"chat","content":"hi"} 这种结构,就必须有对应路由,否则 onMessage 里收到的永远是原始字符串,无法走业务事件分发。
每个事件类(如 ChatEvent)必须实现 __invoke($server, $frame, $data) 方法,且内部调用 $server->push() 或 $this->broadcast() 才真正发出去;只 return 或 echo 完全无效。
启动服务并验证连接链路
方法一:前台启动看日志
执行 php think swoole(前提是已注册 swoole 命令),观察终端是否输出 WebSocket server started 及监听地址。若卡在 “Starting server…” 无下文,大概率是端口被占用或配置中 host/port 写错。
方法二:浏览器直连调试
打开开发者工具 → Network → Filter 输入 ws → 访问 ws://127.0.0.1:9502(端口必须和 config/swoole.php 中 server.port 一致)。看到状态为 101 Switching Protocols 且 WS 标签页出现绿色圆点,才算握手成功。
方法三:前端发心跳保活
WebSocket 服务默认开启 ping/pong 心跳(Swoole 4.5+),前端必须每 25 秒内主动 send 一次 {"type":"ping"},否则连接会在 30 秒后被服务端强制关闭,表现为页面突然断连且自动重试失败。



















