WebSocket服务不能用php -S运行,必须用Ratchet配合ReactPHP事件循环;需正确嵌套WsServer与HttpServer,禁用阻塞操作,配置守护进程、心跳及Nginx代理参数。

WebSocket 服务不能靠 php -S 内置服务器跑
PHP 自带的 CLI 服务器(php -S)只处理 HTTP 请求,不支持 WebSocket 握手和长连接维持。Ratchet 是基于 ReactPHP 的异步库,必须运行在支持事件循环的环境中,否则连接会立刻断开或卡在 101 Switching Protocols 阶段。
实操建议:
- 用
composer require cboden/ratchet安装,别用过时的guzzlehttp/ratchet(已废弃) - 启动命令必须是
php your-server.php,不是php -S - 确保 PHP 版本 ≥ 7.4(Ratchet 0.4+ 要求),且启用了
ext-sockets和ext-mbstring - 开发时别用 Apache/Nginx 反向代理 WebSocket 流量——它们默认不透传 Upgrade 头,容易出现
Connection closed before receiving a handshake response
WsServer 和 HttpServer 必须套在一起用
Ratchet 不提供“开箱即用”的 WebSocket 服务器类。你写的逻辑要先包进 WsServer,再塞进 HttpServer,因为 WebSocket 协议复用 HTTP 升级流程,底层仍需 HTTP 解析器处理握手请求。
常见错误现象:只 new WsServer 就监听端口,结果浏览器连不上,curl 返回 HTTP/1.1 426 Upgrade Required 或直接 timeout。
立即学习“PHP免费学习笔记(深入)”;
正确结构示例:
use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
use MyApp\Chat;
$server = IoServer::factory(
new HttpServer(new WsServer(new Chat())),
8080
);
注意:Chat 类必须实现 MessageComponentInterface,否则启动报 Argument 1 passed to Ratchet\WebSocket\WsServer::__construct() must implement interface Ratchet\MessageComponentInterface。
onOpen/onMessage 里不能阻塞,也不能用 sleep() 或同步 DB 查询
Ratchet 基于 ReactPHP 的单线程事件循环,所有回调都在同一个进程里串行执行。一旦某个 onMessage 里调了 file_get_contents('https://...') 或没设超时的 PDO 查询,整个服务就卡住,新连接无法接入,已有连接收不到响应。
使用场景限制:
- 需要发 HTTP 请求?用
React\Http\Browser替代file_get_contents - 查数据库?用
react/mysql或amphp/mysql这类异步驱动,别碰mysqli或原生PDO - 想延时广播?用
$server->loop->addTimer(1, function () { ... }),不是sleep(1) - 日志写文件?用
React\Stream\WritableResourceStream异步写,避免file_put_contents阻塞
生产环境必须加守护、心跳和反向代理配置
本地跑通不等于能上线。Ratchet 进程挂了不会自启;客户端网络抖动时连接不主动断开,服务端还留着僵尸连接;Nginx 默认砍掉空闲 60 秒的连接。
关键配置点:
- 用
supervisord或systemd管理进程,禁止直接nohup php server.php & - 客户端必须发 ping,服务端在
onMessage里识别\x89帧并回 pong(Ratchet 不自动处理心跳,得自己判断$msg instanceof \Ratchet\RFC6455\Messaging\Ping) - Nginx 配置里必须有:
proxy_http_version 1.1;、proxy_set_header Upgrade $http_upgrade;、proxy_set_header Connection "upgrade";,缺一不可 - 连接数上限受 PHP
ulimit -n和系统net.core.somaxconn限制,别等Too many open files才查
最常被忽略的是连接泄漏:onClose 里没 unset 客户端引用、没清空房间数组、没关掉关联的 timer,跑几天内存就涨到几个 GB。











