UDP服务器必须显式指定SWOOLE_SOCK_UDP,否则默认创建TCP套接字,无法监听UDP流量;需用on('packet')处理收包,sendto()必须带完整IP和端口,且不可用浏览器测试。

UDP服务器必须显式指定SWOOLE_SOCK_UDP,否则根本不会监听UDP流量——这是90%启动失败或收不到包的根源。
创建Server时漏传SWOOLE_SOCK_UDP会直接失效
很多人复制TCP代码改端口就跑,结果ss -uln查不到监听,php -r客户端也收不到响应。问题不在逻辑,而在构造函数参数本身。
-
new Swoole\Server('0.0.0.0', 9502)→ 默认走SWOOLE_SOCK_TCP,系统压根不创建UDP套接字 -
new Swoole\Server('0.0.0.0', 9502, SWOOLE_PROCESS, SWOOLE_SOCK_UDP)→ 正确,底层才真正绑定UDP端口 - 模式推荐用
SWOOLE_PROCESS;SWOOLE_BASE下on('packet')行为不稳定,可能丢包或回调错乱
on('packet')是唯一入口,没有on('connect')或on('close')
UDP无连接,所有数据都靠on('packet')一次触发处理。别在别处写逻辑,也别指望靠$fd做状态管理——它根本不存在。
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
-
$client_info['address']和$client_info['port']每次都是新值,不能缓存复用(NAT环境下端口随时变) - 空包、超长包(>65507字节)必须主动拦截:
if (empty($data) || strlen($data) > 65507) { return; } - 禁止在
on('packet')里做同步IO(如file_get_contents),会阻塞整个worker;要用go()或投递task
sendto()必须带完整地址+端口,顺序不能错
TCP用send($fd, $data)靠连接上下文发,UDP没$fd,所有发送必须靠sendto($ip, $port, $data),三者缺一不可。
- 漏掉
$port或传0→sendto()返回false,但不报错,容易误判成功 - 误用
$serv->send($fd, $data)→ 直接报Warning: Swoole\Server::send(): fd[xxx] is not connected -
sendto()不支持广播封装,需自行构造广播地址(如255.255.255.255)并确保网卡允许
测试必须用CLI,浏览器完全无效
HTTP请求走TCP,浏览器根本发不出UDP包。常见错误是写个TP6控制器调Swoole\Client再用浏览器访问——那只是个HTTP请求,和UDP零关系。
- 快速验证命令:
php -r "$c=new Swoole\Client(SWOOLE_SOCK_UDP); $c->connect('127.0.0.1',9502); $c->send('test'); echo $c->recv();" - 确认服务真在跑:
ss -uln | grep :9502,看到udp行才说明监听成功 - 若
recv()超时,先检查防火墙、SELinux、Docker网络隔离是否放行UDP端口
最易被忽略的是$client_info的动态性——它不是连接句柄,而是每次收包的快照;哪怕同一台客户端,两次包的port也可能不同。拿它当“会话ID”用,不出问题才怪。

















