udp服务器必须用swoole_sock_udp创建,不能复用tcp写法;需显式指定协议类型、使用on('packet')处理收包、sendto()发送时必须带完整地址和端口,且不可用浏览器测试。

UDP服务器必须用 SWOOLE_SOCK_UDP 创建,不能复用TCP写法
很多人照着TCP服务代码改UDP,结果启动报错或收不到包——根本原因是构造Swoole\Server时漏了协议类型参数。TCP默认就是SWOOLE_SOCK_TCP,但UDP必须显式传入SWOOLE_SOCK_UDP,否则底层根本不识别为UDP套接字。
-
new Swoole\Server('0.0.0.0', 9502)→ 默认是TCP,监听后客户端发UDP包会直接被系统丢弃 -
new Swoole\Server('0.0.0.0', 9502, SWOOLE_PROCESS, SWOOLE_SOCK_UDP)→ 正确,启用UDP监听 - 注意
SWOOLE_PROCESS模式推荐用于UDP,协程模式(SWOOLE_BASE)下on('packet')回调行为不一致,容易误判连接状态
on('packet') 是唯一入口,没有 on('connect') 或 on('close')
UDP无连接,所以on('connect')、on('close')这些TCP专属事件在UDP里根本不会触发。所有逻辑都得塞进on('packet')回调里处理,包括校验、应答、限流甚至“模拟会话”。
-
$client_info['address']和$client_info['port']是每次收包时动态提供的,不能缓存复用——客户端可能NAT换端口,下次包就不是同一个$port - 空数据或超长包要主动拦截:
if (empty($data) || strlen($data) > 65507) { $serv->sendto(...'bad packet'); return; } - 别试图在
on('packet')里做阻塞IO(比如file_get_contents),会卡死整个worker;需异步调用或投递到task进程
sendto() 必须带完整地址+端口,且不能用 send()
TCP用send($fd, $data)靠连接上下文发数据,UDP没$fd,所有发送必须靠sendto($ip, $port, $data),参数一个都不能少,顺序也不能错。
- 写成
$serv->sendto($client_info['address'], $client_info['port'], 'ok')→ 正确 - 漏掉
$port或传0→ 发送失败,sendto()返回false,但不会抛异常 - 用
$serv->send($fd, ...)→ 直接报错:Warning: Swoole\Server::send(): fd[xxx] is not connected - 若需广播,得自己循环调用
sendto(),Swoole不提供原生UDP广播封装
客户端测试别用浏览器,用 php -r 或 netcat
浏览器走HTTP/TCP,根本发不出UDP包。常见错误是写个TP6控制器调Swoole\Client然后用浏览器访问——这实际走的是HTTP请求,和UDP完全无关。
- 快速验证命令:
echo "ping" | nc -u 127.0.0.1 9502(Linux/macOS) - PHP CLI一行测:
php -r "$c=new Swoole\Client(SWOOLE_SOCK_UDP); $c->connect('127.0.0.1',9502); $c->send('test'); echo $c->recv();" - 如果
recv()超时,先检查服务端是否真在运行:ss -uln | grep :9502,确认端口被swoole进程监听










