Swoole UDP服务器必须显式指定SWOOLE_SOCK_UDP类型,因UDP无连接、无状态,构造时传错协议类型(如误用TCP)会导致onPacket不触发、sendto失效;监听须用0.0.0.0而非127.0.0.1,客户端识别依赖$client_info['address']和['port'],回复必须用sendto($addr,$port,$data)而非send,且需注意防火墙、权限及UDP包大小限制。

Swoole 的 UDP 服务器不是“开箱即用”的 HTTP 那种默认行为,它底层是无连接、无状态的,写错一个参数或忽略生命周期细节,recv 就收不到数据,sendto 就发不出去——这几乎是面试现场最常卡住的点。
为什么 Swoole\Server 构造时必须传 SWOOLE_SOCK_UDP
UDP 服务端和 TCP 服务端在 Swoole\Server 内部走完全不同的事件分发路径。传错类型(比如误用 SWOOLE_SOCK_TCP)会导致:onReceive 根本不触发,server->getClientInfo() 返回 false,日志里也看不到错误提示。
- 必须显式指定:
new Swoole\Server('0.0.0.0', 9502, SWOOLE_PROCESS, SWOOLE_SOCK_UDP) -
SWOOLE_BASE模式下不支持 UDP,只能用SWOOLE_PROCESS - 监听地址不能写
127.0.0.1然后从外网发包测试——本地回环和实际网卡行为不一致,建议直接用0.0.0.0
onReceive 回调里的 $fd 不是连接 ID,而是对端 IP+端口的哈希值
UDP 没有连接概念,所以 $fd 在这里只是个临时标识符,每次收到不同客户端的数据,$fd 值都可能变。你不能拿它做长期缓存或绑定用户状态。
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
-
$fd仅在当前onReceive调用中有效,不能存到全局数组或static变量里复用 - 要识别客户端,必须解析
$client_info['address']和$client_info['port']字段 - 如果需要会话管理(比如游戏心跳),得自己用
ip:port当 key 存到Server::$connections或 Redis 中
用 $server->sendto() 回复时,目标地址必须和 onReceive 提供的一致
UDP 是“谁发来就往谁那回”,但很多人习惯性写成 $server->sendto($fd, $data),这是错的——$fd 对 UDP 无效,sendto 必须带完整地址信息。
- 正确写法:
$server->sendto($client_info['address'], $client_info['port'], $data) - 漏掉
$client_info或用错字段名(比如写成ip而非address)会导致静默失败 - 如果目标地址是 IPv6,
$client_info['address']是带方括号的字符串(如[::1]),直接拼接端口会出错,需用inet_pton+inet_ntop处理
调试时收不到包?先关掉 iptables 和 SELinux,再检查 bind 权限
UDP 包在进到 PHP 层之前,就被系统拦截了。常见现象是 netstat -uln | grep 9502 看不到监听,或者 tcpdump -i any udp port 9502 能抓到包但 PHP 不触发 onReceive。
- Linux 上非 root 用户无法 bind 1024 以下端口,别用 53 或 67 测试
- CentOS 默认开启 SELinux,会阻止 PHP 进程监听 UDP 端口,临时关闭:
setenforce 0 -
firewalld或ufw可能放行了 TCP 却没开 UDP,确认规则含udp --dport 9502
net.core.rmem_max 是 212992 字节,但应用层单次 recv 实际受 SO_RCVBUF 和 MTU 共同影响;超过 65507 字节的 UDP 包会被截断或丢弃,而 Swoole 不报错,只给你一个不完整的 $data。

















