hyperf多协议监听必须在config/autoload/server.php中显式配置混合服务器,type设为server::server_websocket,同时注册on_request和on_hand_shake回调,并启用open_websocket_protocol=true,不可拆分为多个server块。

Hyperf 多协议监听必须改 server.php,不是靠启动参数
Hyperf 默认只启一个服务进程,php bin/hyperf.php start 不会自动拉起多个端口——哪怕你写了多个 servers 配置,只要没在 config/autoload/server.php 里显式声明,就等于没写。常见错误是把 HTTP 和 WebSocket 分开写成两个独立配置块,但 type 写错、callbacks 缺失或没启用 open_websocket_protocol,结果只跑通一个协议。
-
type必须设为Server::SERVER_WEBSOCKET才能同时处理 HTTP + WS,不能用SERVER_HTTP或SERVER_TCP -
callbacks中Event::ON_REQUEST和Event::ON_HAND_SHAKE必须同时存在,缺一不可 -
settings里必须加'open_websocket_protocol' => true,否则 Swoole 不识别 Upgrade 请求 - 端口复用只支持单个
server块,不要拆成两个name不同的 server(比如http和ws),否则会起两个进程,资源浪费且 FD 冲突
混合监听时 WebSocket 握手失败的三个硬性条件
9501 端口上 HTTP 能通、WS 连不上?大概率是握手阶段被拦截。Hyperf 的 onHandShake 方法依赖底层 Swoole 完成协议升级,但以下三点任一不满足,就会返回 400 或直接断连:
- 请求头必须带
Upgrade: websocket和Connection: Upgrade,curl 测试时得加-H "Upgrade: websocket" -
Sec-WebSocket-Key必须由客户端生成(浏览器自动带,Postman 要手动填),服务端不校验内容,但缺失就拒收 - Hyperf 的
WebSocketServer\Server类要求路由匹配到/ws(或你自定义的路径),如果onHandShake回调里没做路径判断,或中间件提前终止了请求,握手就卡在 pre-flight 阶段
HTTP 和 WebSocket 共享连接池但不能共用协程上下文
同一个端口监听下,HTTP 请求和 WebSocket 连接共享 Swoole 的 worker 进程和连接数限制,但它们的生命周期完全不同:HTTP 是短连接,WS 是长连接。这意味着:
- 不要在 HTTP 中间件里操作
$request->getAttribute('websocket')—— 这个属性只在 WS 握手成功后才注入 - WebSocket 的
onMessage回调里无法访问 HTTP 的Session或Cookie解析结果,得自己从$frame->data解包或走 token 鉴权 - 如果开了
enable_default_metric,{app_name}_request_count指标会统计所有进来的请求(包括 Upgrade),但{app_name}_websocket_connection_total才是真实连接数,别混着看
WSL2 下监听 0.0.0.0:9501 但宿主机 curl 不通?检查这三处
WSL2 网络是 NAT 模式,默认不转发非 localhost 的入站连接。即使 Hyperf 明确绑定了 0.0.0.0,Windows 宿主机也访问不到,除非:
- 确认 WSL2 的防火墙没拦:在 PowerShell 运行
netsh interface portproxy show v4tov4,看有没有 9501 的转发规则 - 手动加一条端口转发:
netsh interface portproxy add v4tov4 listenport=9501 listenaddress=0.0.0.0 connectport=9501 connectaddress=127.0.0.1 - Hyperf 启动后,进 WSL2 终端执行
ss -tlnp | grep 9501,确认监听地址确实是*:9501而不是127.0.0.1:9501—— 后者在 WSL2 里等同于只限本机
多协议监听本身不复杂,真正卡点永远在协议细节和环境边界上。WSL2、Docker、SELinux 这些层叠的网络控制,比代码逻辑更容易让服务“看起来在跑,实际没生效”。










