应使用 onconnect/onclose 手动维护连接计数器或 swoole ≥4.5.0 的 $worker->connectionmap;$worker->connections 仅含已握手完成的活跃连接,会遗漏握手未完成或正在关闭的连接。

直接用 $worker->connections 遍历会漏掉连接
因为 $worker->connections 是一个引用数组,只包含「当前已握手完成、处于 active 状态」的连接;新连接刚建立但还没完成 SSL 握手、或正在关闭过程中的连接(如 onClose 已触发但资源未完全释放),都不会出现在这个数组里。
常见错误现象:你写了个定时器去遍历 $worker->connections 统计在线数,结果比实际 WebSocket 客户端数少 1–2 个,尤其在高并发连接/断开瞬间特别明显。
- 使用场景:需要精确统计或广播时,不能只依赖
$worker->connections - 真正可靠的连接数来源是
$worker->connectionMap(Swoole ≥ 4.5.0)或通过onConnect/onClose手动维护计数器 - 如果用的是 Swoole 4.4.x 或更早版本,
$worker->connectionMap不可用,必须自己维护$onlineCount变量并加锁(推荐swoole_atomic)
onConnect 和 onClose 必须成对管理连接状态
这是最稳定、兼容性最好的方式——不依赖内部数组结构,也不受握手阶段影响。只要连接建立成功(哪怕还没发第一个包),onConnect 就会触发;只要底层 socket 关闭(不管是否优雅),onClose 都会回调。
- 容易踩的坑:在
onClose里直接 unset 全局连接数组,但没考虑多进程下变量不共享 —— 每个 worker 进程要维护自己的连接映射 - 参数差异:
$fd在onConnect和onClose中一致,但$from_id只在 UDP 场景有用,TCP/WebSocket 下可忽略 - 性能影响:频繁读写全局数组本身开销不大,但若在里面做耗时操作(比如查 DB、调远程接口),会阻塞事件循环
想批量操作所有连接?别直接 foreach $worker->connections
直接遍历 $worker->connections 并调用 $worker->send($fd, $data) 看似简单,但存在两个硬伤:一是可能发给已断开但尚未被清理的 $fd,导致 ERROR: send() failed, errno=32 (Broken pipe);二是并发修改数组时(比如另一协程正在 close),可能触发 PHP 的「array modified during foreach」警告(虽然不致命,但干扰日志)。
- 实操建议:先用
array_keys($worker->connections)拿出 fd 列表,再逐个isset($worker->connections[$fd])校验后再操作 - 更稳妥的做法是结合自维护的连接列表 +
isEstablished()辅助判断(Swoole ≥ 5.0.0 提供了$worker->isEstablished($fd)) - 广播场景下,优先用
$worker->push($fd, $data)而不是$worker->send(),前者会自动跳过无效 fd
Swoole 版本差异直接影响你能用什么方法
低于 4.5.0 的版本没有 $worker->connectionMap,也没有 isEstablished(),连 $worker->connections 的内部结构都可能随小版本微调。升级不是可选项,而是必要前提。
- 4.4.x:只能靠
onConnect/onClose+ 原子计数器,且要注意$worker->connections[$fd]可能为 null(握手未完成) - 4.5.0+:可用
$worker->connectionMap获取全量连接信息(含握手状态),但注意它返回的是Connection对象,不是原始 fd 数组 - 5.0.0+:新增
$worker->isEstablished($fd)和$worker->exist($fd),判断更精准;同时$worker->connections行为更稳定
复杂点在于:连接状态不是非黑即白的布尔值,而是一个有生命周期的流程。很多问题不是代码写错了,而是没意识到「连接已创建」和「连接可通信」之间存在时间差。










