swoole中http与websocket可共享端口,websocket通过http upgrade实现;新增tcp端口需显式set配置否则继承http行为。

HTTP 和 WebSocket 不能混在同一个端口上靠“手动协议识别”来共存——但 Swoole 已经帮你自动处理好了,根本不需要你区分。 真正要关注的,是主服务类型决定默认协议行为,而新监听端口是否显式 set 覆盖了它。
HTTP 主服务 + listen 新 TCP 端口,默认还是 HTTP 协议
很多人以为 $http_server->listen(...) 新加的端口天然就是裸 TCP,其实不是。只要没调 set 清除协议选项,它会完整继承主 Swoole\Http\Server 的配置,包括 open_http_protocol 和内部的 request 解析逻辑。
这意味着:如果你用 $http_server->listen("0.0.0.0", 9502, SWOOLE_SOCK_TCP) 启了一个新端口,又没调 $port->set([]),那么发一个原始 TCP 包过去,on('receive') 根本不会触发——Swoole 会尝试按 HTTP 协议解析,失败后直接丢弃连接。
- 必须显式调用
$port->set([])或至少$port->set(['open_eof_check' => false])关闭协议层解析 - 否则即使注册了
on('receive'),也收不到数据 - 主服务是
Swoole\Http\Server,新端口不重置 set,就永远走 HTTP 流程
WebSocket 握手和帧通信都在同一 HTTP 端口内完成
HTTP 和 WebSocket 共享端口不是“多路复用”,而是协议升级(Upgrade)。Swoole 的 Swoole\Http\Server 内置识别 Upgrade: websocket 请求头,并自动切换到 WebSocket 状态机。你不需要、也不应该为 WebSocket 单独开一个端口。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
常见错误是:开了两个端口,一个配 on('request'),一个配 on('open'),结果发现 WebSocket 客户端连不上——因为客户端只连了 HTTP 端口,却期待服务器在另一个端口响应 WebSocket 帧。
- WebSocket 客户端 URL 是
ws://host:port/path,必须指向Swoole\Http\Server绑定的那个端口 -
on('open')、on('message')回调只在该端口上触发,与 HTTP 请求共存 - 不要用
listen()为 WebSocket 单独加端口,那是冗余且失效的
想同时提供 HTTP/WebSocket + 自定义 TCP 协议?必须分清主从关系
正确做法是:以 Swoole\Http\Server 为主服务,它负责 9501 端口的 HTTP + WebSocket;再用 addListener(或 listen)加一个纯 TCP 端口,比如 9502,并立即用 set 切换协议模式。
关键点在于:新端口的协议行为完全由你 set 的参数控制,而不是由“监听顺序”或“变量名”决定。
- 用
$tcp_port = $http_server->addListener("0.0.0.0", 9502, SWOOLE_SOCK_TCP) - 立刻执行
$tcp_port->set(['open_length_check' => true, 'package_length_type' => 'N'])或其他包协议配置 - 然后绑定
on('receive')处理二进制流,此时它才真正是 TCP 端口 - 如果漏掉
set,这个端口会静默拒绝非 HTTP 流量
最容易被忽略的是:新监听端口的协议开关(如 open_http_protocol、open_websocket_protocol、open_length_check)互斥且不可叠加。一旦主服务是 HTTP 类型,所有未显式 set 的端口都会带 HTTP 行为——这不是 bug,是设计使然。你得亲手关掉它,才能打开自己要的协议。










