thinkphp 本身不提供 websocket 接口实现能力,所有版本都必须依赖外部常驻进程框架(如 swoole 或 workerman),tp 只能作为业务逻辑层参与通信——这是根本前提,绕不开。

为什么不能在控制器里 new swoole_websocket_server()
这不是语法错误,而是模型冲突:TP 是请求-响应生命周期,每次 HTTP 请求结束,PHP 进程就销毁;而 swoole_websocket_server 必须长驻内存、持续监听连接。硬塞进控制器会导致服务启动即退出,或被 PHP-FPM 主动 kill。
常见错误现象:
- 执行命令后终端立刻返回,无任何监听日志
- 浏览器连
ws://127.0.0.1:9501报ERR_CONNECTION_REFUSED - 日志里出现
Segmentation fault或Worker process exit
正确做法是用命令行独立启一个常驻进程,比如:
php think websocket:start
这个命令背后必须是继承 think\console\Command 的类,内部实例化 swoole_websocket_server 并调用 start(),且不能有任何 HTTP 响应逻辑混入。
ThinkPHP 6 + think-swoole 的配置陷阱
TP6 官方推荐用 topthink/think-swoole,但早期版本(v2.0.x)的配置项命名混乱,极易配错:
-
websocket.enable(v2.0.4+)和旧版的websocket.enabled不兼容,写错直接静默失效 -
server.type必须是SWOOLE_SOCK_TCP,设成SWOOLE_SOCK_UDP会握手失败 -
websocket.handler类必须实现HandlerInterface,且方法签名不能少参数(例如onOpen($server, $request)少$request会 crash) - 若用 Redis pub/sub 中转消息,
redis.host和redis.port必须在config/redis.php里显式配置,think-swoole 不自动读取 .env 中的 REDIS_* 变量
建议直接查 vendor/topthink/think-swoole/src/Websocket.php 看它实际读哪些键,别只抄文档。
WebSocket 连接成功但收不到消息?先盯住这三处
80% 的“连上了却没反应”问题,根源不在 TP 代码,而在链路断点:
- Redis 是否通?运行
redis-cli -h 127.0.0.1 -p 6379 ping,必须返回PONG;否则publish消息发不出去 - WebSocket 进程是否成功
subscribe?查日志里有没有类似Subscribed to channel: message:all的记录,没订阅就收不到 TP 主动推送的消息 - Nginx 反向代理是否透传协议头?必须在 location 块里加这两行:
proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection "upgrade";
漏掉任意一个,浏览器控制台报net::ERR_SSL_PROTOCOL_ERROR(wss)或直接ERR_CONNECTION_REFUSED(ws)
心跳、FD/UID 映射、离线消息这些细节才真正卡人
调试阶段能连能发,不代表线上稳。真实压力下暴露的问题往往藏在边界场景里:
-
onClose回调里没清理fd → uid映射表,导致用户重连后旧 FD 还挂着,消息发串 - 心跳间隔设 30 秒,但客户端网络抖动丢包两次,服务端就误判离线并踢出房间——得配合
ping_timeout和ping_max参数一起调 - 离线消息只存 Redis List,没加过期时间,用户半年没上线,List 膨胀到 GB 级,
LPOP卡死整个服务 - 宝塔或云服务器安全组开了 9501 端口,但系统防火墙(
ufw或firewalld)没放行,客户端连不上还反复查代码
这些不是“会不会”的问题,而是“压上去之后才发现原来没想全”的地方。别等流量来了再补。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











