websocket子协议是rfc 6455定义的标准协商机制,通过sec-websocket-protocol头部在握手阶段声明应用层语义(如"json"、"binary"),仅用于约定数据格式与解析规则,不改变传输或影响连接建立,不可用于鉴权或路由。

WebSocket subprotocol 是什么,为什么需要它
WebSocket subprotocol 不是 Swoole 特有的概念,而是 RFC 6455 规范定义的标准字段,用于在 HTTP Upgrade 握手阶段协商应用层语义。浏览器发起 WebSocket 连接时,可通过 Sec-WebSocket-Protocol 请求头声明支持的子协议(如 "json"、"graphql-ws"),服务端在响应中选择其一返回,双方后续通信即按该协议约定解析数据。
它不改变底层传输,也不影响连接建立流程,只是一种“口头约定”——告诉对方:“接下来我们按 JSON 格式传消息” 或 “按自定义二进制帧结构来”。Swoole 本身不解析或校验子协议内容,只是透传和匹配。
Swoole 中如何设置和获取 subprotocol
Swoole 的 WebSocket\Server 在握手阶段提供两个关键点:
-
onHandShake回调中,可通过$request->header['sec-websocket-protocol']读取客户端声明的子协议列表(逗号分隔字符串) -
onOpen回调中,$fd对应的连接已建立,但此时子协议已协商完成,无法再修改 - 若需响应特定子协议,必须在
onHandShake中手动构造 HTTP 响应头,写入Sec-WebSocket-Protocol: xxx,并调用$response->end()终止握手
示例(手动处理 handshake):
$server->on('handshake', function ($request, $response) {
$protocols = explode(',', $request->header['sec-websocket-protocol'] ?? '');
$chosen = in_array('chat-v2', $protocols) ? 'chat-v2' : 'chat-v1';
$response->header('Sec-WebSocket-Protocol', $chosen);
$response->status(101);
$response->end();
});
注意:一旦启用自定义 onHandShake,Swoole 默认的握手逻辑就失效,包括 Origin 校验、cookie 解析等都需自行实现。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
常见误用:把 subprotocol 当成路由或鉴权手段
不少开发者试图用子协议区分业务类型(比如 "admin"、"user"),这是危险的:
- 子协议字段完全由客户端控制,不可信,不能用于权限判断
- 浏览器端可任意伪造
Sec-WebSocket-Protocol头,Node.js 客户端更无限制 - Swoole 不会拦截或验证子协议合法性,也不会自动绑定到某类 Controller 或 Route
- 真正需要鉴权或路由,应在
onOpen中解析首次消息、检查 token,或结合cookie/url query参数
子协议只适合做轻量级语义标识,例如:"binary" 表示后续发 Protobuf 数据,"json" 表示 UTF-8 文本,"mqtt" 表示模拟 MQTT over WS —— 仅此而已。
EasySwoole 框架里 subprotocol 怎么用
EasySwoole 的 WebSocketServer 默认不暴露 handshake 控制权,它把子协议处理封装进了 WebSocketEvent::onHandShake() 钩子,但该钩子默认为空。若要启用:
- 必须重写
onHandShake方法,并调用$response->header()显式设置 - 框架不会自动保存选中的子协议到连接上下文,需自己存入
$server->setConnectionProperty($fd, 'subprotocol', $chosen) - 后续在
onMessage中通过$server->getConnectionProperty($fd, 'subprotocol')获取,才能做差异化解析
容易忽略的一点:EasySwoole 的 WebSocketParser 默认只处理 JSON,如果你用了 "binary" 子协议,却还在 parser 里尝试 json_decode(),就会静默失败。










