不能用原生PHP写WebSocket服务端,因缺乏协议支持,手写易漏握手头、帧解析等细节;必须用Swoole v4.8+的WebSocket\Server,需开启协程、监听0.0.0.0、配好Nginx代理头及SSL、防火墙放行端口,并正确设置心跳参数防fd泄漏。

为什么不能用原生 PHP 写 WebSocket 服务端
PHP 原生不支持 WebSocket 服务端,fsockopen 或 stream_socket_server 手写握手几乎必踩坑:漏掉 Sec-WebSocket-Key 的 base64-sha1 转换、忘记返回 HTTP/1.1 101 Switching Protocols、忽略 Upgrade: websocket 和 Connection: upgrade 头——这些都会导致前端报错 Connection closed before receiving a handshake response。更麻烦的是,分片帧、ping/pong 心跳、关闭帧等协议细节全得自己解析,极易出错且无法稳定上线。
必须用 Swoole v4.8+ 启动 Swoole\WebSocket\Server
目前唯一生产可用的方案是 Swoole(v4.8 或 v5.x),它内置完整 WebSocket 协议栈,自动处理握手、帧解包、心跳、连接管理。实操前先确认:
-
php -m | grep swoole输出版本 ≥ 4.8 -
php --ri swoole | grep coroutine显示coroutine => enabled(协程支持必须开启) - 监听地址必须是
'0.0.0.0',不能只写'127.0.0.1',否则浏览器跨网段连接会被拒绝 - 启动前检查端口是否空闲:
lsof -i :9501或netstat -tuln | grep 9501
最小可运行示例:
$server = new Swoole\WebSocket\Server('0.0.0.0', 9501);
$server->set([
'heartbeat_check_interval' => 60,
'heartbeat_idle_time' => 600,
]);
$server->on('open', function ($server, $request) {
echo "Client {$request->fd} connected\n";
});
$server->on('message', function ($server, $frame) {
$server->push($frame->fd, "Echo: {$frame->data}");
});
$server->on('close', function ($server, $fd) {
echo "Client {$fd} disconnected\n";
});
$server->start();
前端连不上?先查这三处代理和协议配置
错误 Connection closed before receiving a handshake response 99% 不是代码问题,而是环境配置断链:
立即学习“PHP免费学习笔记(深入)”;
- Nginx 反向代理未透传关键头:必须显式添加
proxy_set_header Upgrade $http_upgrade;和proxy_set_header Connection "upgrade"; - 前端页面是
https://,却用ws://连接——浏览器直接拦截;必须配 Nginx 终止 SSL,后端仍走ws://,前端用wss:// - 防火墙或宝塔面板未放行 9501 端口(尤其是云服务器安全组)
push() 广播时别阻塞主线程
直接在 on('message') 回调里遍历所有连接调 $server->push(),会串行发送,高并发下延迟飙升。正确做法是协程化广播:
- 用
Swoole\Coroutine\run()包裹批量推送逻辑 - 或改用
$server->task()把广播任务投递到 task 进程异步执行 - 避免在回调中做数据库查询、HTTP 请求等同步 I/O,否则整个事件循环卡住
真正容易被忽略的点是:心跳参数 heartbeat_check_interval 和 heartbeat_idle_time 必须成对设置,否则断连的 fd 不会自动回收,fd 数持续增长直至耗尽。这不是性能问题,是资源泄漏。



















