PHP不原生支持WebSocket客户端长连接,需用Workerman等框架构建常驻进程实现握手、帧解析、心跳与重连;不可用cURL或Guzzle模拟,否则会因协议不支持或缺少事件循环而失败。
PHP 本身不原生支持 WebSocket 客户端长连接(fsockopen 或 stream_socket_client 只能做一次握手+单次通信),所谓“PHP 连 WebSocket 并保持长连”,实际是指用 PHP 启动一个常驻进程,自行处理 WebSocket 握手、帧解析、心跳、重连——不是靠 curl 或普通 HTTP 请求能搞定的。
PHP 没有像 Node.js 的 ws 或 Python 的 websockets 那样开箱即用的异步 WebSocket 客户端库。主流方案是基于 ReactPHP 或 Workerman 构建事件循环,手动完成:
Sec-WebSocket-Key 和 base64 签名)0x9),并响应 pong(0xA)直接用 file_get_contents 或 cURL 访问 ws:// 地址会报错:Unsupported protocol: ws —— 因为它们根本不支持 WebSocket 协议栈。
Workerman 是国内最成熟的 PHP 长连接解决方案,它内置 WebSocket 客户端组件,自动处理帧、ping/pong、重连逻辑。关键点:
Worker::runAll() 启动常驻进程,不能跑在 FPM/CLI 一次性脚本里$client->onClose 触发后需主动调用 $this->reconnect(),不能依赖超时自动重试onMessage 中执行耗时操作(如数据库查询),否则阻塞整个事件循环示例片段(简化):
$client = new \Workerman\Connection\AsyncTcpConnection('ws://echo.websocket.org');
$client->onConnect = function($connection) {
echo "Connected\n";
};
$
client->onMessage = function($connection, $data) {
echo "Received: $data\n";
};
$client->onClose = function($connection) {
echo "Disconnected, reconnecting...\n";
// 手动重连逻辑需在此处触发
};
$client->connect();
常见误区:用 GuzzleHttp\Client 发起 GET 请求,加 Upgrade: websocket 头。这只会得到一个 400 或 501 错误,因为:
Operation now in progress
如果你只是想“从 PHP 后端向 WebSocket 服务发一条指令”,正确做法是:让前端 JS 直连 WS,PHP 通过 REST API 通知前端,再由前端发 WS 消息;或者用 Redis Pub/Sub 做桥接,避免 PHP 直连 WS。
真正可靠的 PHP WebSocket 客户端长连,本质是写一个守护进程,而非常规 Web 请求。漏掉事件循环、帧解析或心跳机制中的任意一环,连接大概率在 30–60 秒内被中间设备静默断开,且无错误提示。