websocket 握手
WebSocket 握手是客户端和服务器建立持久连接的桥梁。它利用标准的 HTTP/HTTPS 协议发起,通过 Upgrade 机制将连接从 HTTP 升级为 WebSocket,随后双方即可进行全双工实时通信。 [1, 2, 3, 4, 5]
握手流程分解
整个握手过程主要分为以下几个关键步骤:
TCP 三次握手:底层先建立可靠的 TCP 连接。
客户端发送请求:客户端(通常为浏览器)发送一个特制的 HTTP/1.1 GET 请求。
服务端响应:服务端验证请求后,返回状态码为
101 Switching Protocols的响应,标志握手成功。 [1, 2, 3]
报文结构示例
握手请求与响应包含特定的参数字段,以确保连接安全合法:
1. 客户端握手请求 (Request)
http
GET /chat HTTP/1.1
Host: ://example.com
Upgrade: websocket # 关键:通知服务器升级协议
Connection: Upgrade # 关键:表明连接将被升级
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== # 随机 Base64 字符串,用于服务端校验
Sec-WebSocket-Version: 13 # WebSocket 协议版本号
Origin: http://example.com
请谨慎使用此类代码。
2. 服务端握手响应 (Response)
http
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= # 通过特定算法生成的密钥
Sec-WebSocket-Version: 13
请谨慎使用此类代码。
核心校验机制:Sec-WebSocket-Key
为了防止恶意连接或非 WebSocket 客户端误访问,协议设计了密钥校验:
服务端收到客户端的
Sec-WebSocket-Key后,将其与全局唯一的魔术字符串258EAFA5-E914-47DA-95CA-C5AB0DC85B11拼接。使用 SHA-1 算法计算哈希值。
将哈希值转换为 Base64 编码,并在响应头部的
Sec-WebSocket-Key中返回给客户端。
在 ThinkPHP 6 中使用 think-swoole 扩展时,框架默认内置了对 Socket.IO 协议的支持和封装。 [1]
Socket.IO 在建立连接时,会先发送一个特殊的 HTTP 握手请求(通常带有 ?transport=polling 或 ?transport=websocket 以及 EIO 版本参数)。think-swoole 已经内置了相关控制器,能自动识别并完成 Socket.IO 的双向握手协商(包括分配 sid)。 [1, 2]
为了配合前端的 socket.io-client 顺利连接,后端需要进行如下配置和事件拦截:
一、 核心配置:开启内置的 Socket.IO 处理器
打开 config/swoole.php 配置文件,找到 websocket 项。将 handler 切换为框架专门为 Socket.IO 准备的类:[1, 2, 3]
php
use think\swoole\websocket\socketio\Handler;
return [
// ...
'websocket' => [
'enable' => true,
'handler' => Handler::class, // 核心:开启 Socket.IO 处理器
// ... 其他配置
],
];
请谨慎使用此类代码。
二、 业务层:连接与鉴权
前端连接:使用
socket.io-client连接,建议通过query携带 Token。javascript
const socket = io("ws://127.0.0.1:7001", { query: { token: "..." } });请谨慎使用此类代码。
后端鉴权:在
app\listener\WsConnect.php中利用$ws->getSender()获取 fd,并通过app('request')->param('token')校验。非法连接可调用$ws->close()。 [1, 2, 3]
三、 自定义事件处理
Socket.IO 是事件驱动的。 [1]
注册事件:在
app\event.php中配置。php
'listen' => [ 'swoole.websocket.自定义事件名' => [\app\listener\OnCustomEvent::class], ],请谨慎使用此类代码。
监听处理:在监听器中通过
$ws->emit()回应消息或进行广播。 [1]
四、 关键注意事项