onreceive 是 tcp/udp 通用入口,但仅当未设置 onpacket 时才处理 udp 包;udp 应优先使用语义清晰、参数完整的 onpacket,其每次触发对应一个完整数据报,含 $clientinfo 可直接 sendto 回包。

onReceive 是 TCP/UDP 通用入口,但只在没设 onPacket 时才收 UDP 包
如果你启动的是 SWOOLE_SOCK_UDP 类型的 Server,但没注册 onPacket 回调,Swoole 会自动把收到的每个 UDP 数据包转给 onReceive 处理。这时 $fd 是 -1,$from_id 是 -1,$data 是原始字节流,$clientInfo 不可用——因为 UDP 没有连接上下文。
这种 fallback 行为容易让人误以为 onReceive “支持 UDP”,其实它只是兜底机制。一旦你显式设置了 onPacket,onReceive 就完全不会被 UDP 触发。
- UDP 场景下优先用
onPacket,语义清晰、参数完整(含$clientInfo) - 若误删了
onPacket注册却还按 UDP 逻辑写onReceive,会发现$fd总是 -1、无法$server->send($fd, ...) - TCP 场景下
onReceive的$fd是有效连接标识,可直接用于send/close
onPacket 只属于 UDP,且必须手动启用
onPacket 是 Swoole 专为 UDP 设计的事件,仅当 Server 构造时指定 SWOOLE_SOCK_UDP 或混合协议(如 SWOOLE_SOCK_TCP | SWOOLE_SOCK_UDP)时才可能触发。TCP 服务器即使监听同一端口,也绝不会进 onPacket 回调。
关键点在于:它不依赖连接状态,每次收到一个 UDP 数据包就触发一次,参数固定为 ($server, $data, $clientInfo),其中 $clientInfo 必含 address 和 port,方便直接回包。
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
- 回包必须用
$server->sendto($clientInfo['address'], $clientInfo['port'], $response),不能用send - UDP 是无状态的,
onPacket中不能假设客户端“还在线”或“会重连” - 若需在混合服务器中区分协议,可在
onPacket中检查$clientInfo['server_port']对应的监听类型
协议解析责任完全不同
onReceive 在 TCP 下拿到的是应用层数据流,可能跨多个包(粘包)、也可能一个包里含多个逻辑消息;你需要自己处理拆包、缓冲、协议头校验。而 onPacket 每次只对应一个完整的 UDP 数据报(最大 64KB),天然“一包一消息”,但不保证送达、不保证顺序、不保证不丢包。
- TCP 的
onReceive:适合做长连接服务(如 IM、游戏),但得自己实现帧定界(如长度前缀、分隔符) - UDP 的
onPacket:适合做轻量请求响应(如 DNS 查询、设备心跳),但得容忍丢包、重传需上层实现 - 两者都不做 HTTP 解析——HTTP 协议由
onRequest专用处理,和它们无关
worker 进程内执行,但触发时机不可混用
两个回调都在 worker 进程中同步执行,不会跨进程调度。但它们的触发源头不同:onReceive 由 reactor 线程在 socket 可读时投递,onPacket 则由 reactor 直接从 UDP socket recvfrom 后立即触发。
这意味着:在高并发 UDP 场景下,onPacket 的调用频率直接受网卡收包速率影响,而 TCP 的 onReceive 还受内核 TCP 缓冲区和 Nagle 算法干扰。如果你在 onPacket 里做了耗时操作(比如同步 MySQL 查询),会阻塞整个 worker,导致后续 UDP 包堆积甚至被丢弃。
- UDP 场景下慎用阻塞 I/O,优先用
task投递耗时逻辑 - TCP 场景下
onReceive同样不能阻塞,但可通过defer或协程化缓解 - 别在
onPacket里尝试维护 fd 映射表——UDP 没有 fd 概念,$clientInfo才是唯一身份依据










