这个库是一个针对 RFC6455 规范的协议处理工具。它包含了用于服务器端和客户端端握手以及消息传递协议协商的组件。
在规范中那些存在歧义的部分,在这个库中同样也是不明确的。如何处理这些歧义,取决于具体的实现方式。
这个库是独立的,不依赖于特定的框架,也不处理任何输入/输出操作。HTTP 升级协商相关的功能是通过 PSR-7 接口来处理的。
协议核心定义在 RFC 6455(The WebSocket Protocol),规定了:
- 握手(Handshake):从 HTTP/1.1 升级为 WebSocket。
- 帧(Frame):消息分帧(文本/二进制、掩码、控制帧:Ping/Pong/Close)。
- 安全性:掩码(masking)、扩展(permessage-deflate)、子协议(Sec-WebSocket-Protocol)。
纯手写协议容易出错(半包、碎片、掩码、超时)。Ratchet RFC6455 正是 PHP 生态中最成熟的 I/O 无关协议处理器,它只负责协议逻辑,不处理网络 I/O。
结合 Workerman(高性能异步 PHP 框架),可以轻松实现一个稳定、可扩展的 WebSocket 服务。
安装依赖
composer require workerman/workerman ratchet/rfc6455 guzzlehttp/psr7
完整实现代码(start.php)
<?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;classWebSocketRFC6455{publicstaticfunctioninput($buffer, $connection): int{if (!isset($connection->handshaked)) {return str_contains($buffer, "\r\n\r\n") ? strlen($buffer) : 0; }// 握手后: 全部消费,MessageBuffer 内部处理分帧与缓冲return strlen($buffer); }publicstaticfunctiondecode($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) ?? ''; }publicstaticfunctionencode($data, $connection): string{if (!isset($connection->handshaked)) {return $data; // 握手阶段透传 101 响应 }// 封装为 WebSocket 文本帧 $frame = new Frame($data, true, Frame::OP_TEXT);return $frame->getContents(); }privatestaticfunctionhandleHandshake($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 - 服务器回复:
服务器回复: 收到你的消息 - 开源技术小栈