
这个库是一个针对 RFC6455 规范的协议处理工具。它包含了用于服务器端和客户端端握手以及消息传递协议协商的组件。
在规范中那些存在歧义的部分,在这个库中同样也是不明确的。如何处理这些歧义,取决于具体的实现方式。
这个库是独立的,不依赖于特定的框架,也不处理任何输入/输出操作。HTTP 升级协商相关的功能是通过 PSR-7 接口来处理的。
协议核心定义在 RFC 6455(The WebSocket Protocol),规定了:
纯手写协议容易出错(半包、碎片、掩码、超时)。Ratchet RFC6455 正是 PHP 生态中最成熟的 I/O 无关协议处理器,它只负责协议逻辑,不处理网络 I/O。
结合 Workerman(高性能异步 PHP 框架),可以轻松实现一个稳定、可扩展的 WebSocket 服务。
composer require workerman/workerman ratchet/rfc6455 guzzlehttp/psr7
<?php
/**
* @desc WebSocket 服务 - 基于 Ratchet RFC6455 + Workerman
* @author Tinywan(ShaoBo Wan)
*/
declare(strict_types=1);
require_once__DIR__ . '/vendor/autoload.php';
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
use Ratchet\RFC6455\Handshake\ServerNegotiator;
use Ratchet\RFC6455\Handshake\RequestVerifier;
use Ratchet\RFC6455\Messaging\MessageBuffer;
use Ratchet\RFC6455\Messaging\Message;
use Ratchet\RFC6455\Messaging\Frame;
use Ratchet\RFC6455\Messaging\CloseFrameChecker;
use GuzzleHttp\Psr7\HttpFactory;
class WebSocketRFC6455
{
publicstaticfunction input($buffer, $connection): int
{
if (!isset($connection->handshaked)) {
return str_contains($buffer, "\r\n\r\n") ? strlen($buffer) : 0;
}
// 握手后: 全部消费,MessageBuffer 内部处理分帧与缓冲
return strlen($buffer);
}
publicstaticfunction decode($buffer, $connection): string
{
if (!isset($connection->handshaked)) {
returnself::handleHandshake($buffer, $connection);
}
// 初始化消息队列
if (!isset($connection->messageQueue)) {
$connection->messageQueue = [];
}
// 交给 Ratchet MessageBuffer 解析(内部有 $leftovers 处理半包)
$connection->messageBuffer->onData($buffer);
// 返回队列中的第一条消息
return array_shift($connection->messageQueue) ?? '';
}
publicstaticfunction encode($data, $connection): string
{
if (!isset($connection->handshaked)) {
return $data; // 握手阶段透传 101 响应
}
// 封装为 WebSocket 文本帧
$frame = new Frame($data, true, Frame::OP_TEXT);
return $frame->getContents();
}
privatestaticfunction handleHandshake($buffer, $connection): string
{
$request = \GuzzleHttp\Psr7\Message::parseRequest($buffer);
$factory = new HttpFactory();
$verifier = new RequestVerifier();
try {
$negotiator = new ServerNegotiator($verifier, $factory);
$response = $negotiator->handshake($request);
$connection->messageQueue = [];
$connection->messageBuffer = new MessageBuffer(
new CloseFrameChecker(),
staticfunction (Message $msg, MessageBuffer $buffer) use ($connection) {
$connection->messageQueue[] = $msg->getPayload();
},
null, false, null, null, null, null, null
);
$connection->send(\GuzzleHttp\Psr7\Message::toString($response));
$connection->handshaked = true;
echo"WebSocket 握手成功: {$connection->getRemoteIp()}\n";
return'';
} catch (\Exception $e) {
echo"握手失败: " . $e->getMessage() . "\n";
$connection->close();
return'';
}
}
}
// ==================== Worker 配置 ====================
$worker = new Worker('tcp://0.0.0.0:8486');
$worker->count = 4;
$worker->protocol = WebSocketRFC6455::class;
$worker->onMessage = function (TcpConnection $connection, $data) {
echo"收到消息: [$data]\n";
$connection->send("服务器回复: 收到你的消息 - $data");
};
$worker->onClose = function (TcpConnection $connection) {
echo"连接关闭: {$connection->getRemoteIp()}\n";
};
Worker::runAll();
Step.1 启动服务器
php start.php start
Step.2 浏览器测试
var ws = new WebSocket('ws://127.0.0.1:8486');
ws.onmessage = function(event) {
console.log('开源技术小栈接收消息: ' + event.data);
};
ws.send("开源技术小栈 你好!");
Step.3 调试输出
WebSocket 握手成功: 172.18.0.1收到消息: [开源技术小栈]服务器回复: 收到你的消息 - 开源技术小栈Workerman[start.php] start in DEBUG mode
-------------------------------------------- WORKERMAN --------------------------------------------
Workerman/5.2.2 PHP/8.4.15 (JIT off) Linux/6.18.33.1-microsoft-standard-WSL2
--------------------------------------------- WORKERS ---------------------------------------------
event-loop proto user worker listen count state
select tcp root none tcp://0.0.0.0:8486 4 [OK]
---------------------------------------------------------------------------------------------------
Press Ctrl+C to stop. Start success.
WebSocket 握手成功: 172.18.0.1
收到消息: []
收到消息: [开源技术小栈 你好!]decode() 返回空字符串,Workerman 直接发送 101 响应。MessageBuffer(Ratchet 的核心组件)。input() 让 Workerman 消费完整包。onMessage 回调自动触发,队列中取出 getPayload()。encode() 使用 Ratchet 的 Frame 类封装为 WebSocket 帧。Ratchet RFC6455 + Workerman 是 PHP 中最优雅的 WebSocket 协议实现方式。它把协议层(RFC6455)与传输层(Workerman)完全解耦,让你专注于业务逻辑。
如果你只是想快速搭建聊天室/实时仪表盘,直接用 Workerman 内置 websocket:// 协议(30 行代码)就够了——更简单、更稳定。