udp监听地址必须显式写为ip:port格式,如'127.0.0.1:2345',不可省略ip写成':2345'或'2345';且必须在$context中指定'transport'=>'udp',否则默认按tcp初始化导致onmessage不触发。

UDP监听地址格式必须是 ip:port,不能省略IP
Workerman 的 Worker 构造函数对 UDP 协议要求严格:地址必须显式写出 IP 和端口,比如 '0.0.0.0:2345' 或 '127.0.0.1:2345'。写成 ':2345' 或 '2345' 会直接报错 Invalid address —— 这是新手最常卡住的地方。
原因在于 Workerman 底层调用 PHP 的 socket_bind(),而 UDP socket 不支持“仅端口”的绑定语法(TCP 的 0.0.0.0:port 可简写为 :port,但 UDP 不行)。
-
'0.0.0.0:2345':监听所有网卡,外部可访问(注意防火墙) -
'127.0.0.1:2345':仅本地进程通信,更安全 -
'192.168.1.100:2345':只绑定指定内网 IP,适合多网卡服务器 - 千万别写
':2345'、'udp://:2345'或'2345',全部无效
Worker 实例必须显式指定 $context 并设 transport 为 'udp'
光写对地址还不够。UDP 模式下,Worker 必须通过 $context 明确声明传输协议,否则默认走 TCP,会导致监听成功但收不到包。
正确写法:
$worker = new Worker('udp://0.0.0.0:2345', [
'transport' => 'udp'
]);
错误写法(看似地址对,实则走 TCP):
// ❌ 错误:没传 transport,底层仍按 TCP 初始化
$worker = new Worker('udp://0.0.0.0:2345');
-
transport必须在$context数组里传,不能靠协议前缀自动识别 -
udp://前缀只是习惯写法,Workerman 并不解析它;真正起作用的是transport配置项 - 如果漏掉
transport,onMessage回调永远不会触发,也没有报错,极难排查
接收数据时 $client_info 不含 port 字段?那是你用了旧版
早期 Workerman(onMessage 回调中,$client_info 只有 remote_ip,没有 remote_port。这导致无法回包——因为 UDP 是无连接的,必须知道对方端口才能 sendto。
解决方案只有两个:
- 升级到 Workerman ≥ 4.0.20(推荐),此时
$client_info['remote_port']存在且可用 - 若无法升级,改用
$worker->send($data, $client_info['remote_ip'].':'.$client_info['remote_port'])手动拼地址(但需自行处理端口缺失逻辑)
验证方式:打印 var_dump($client_info),看到 remote_port 字段才算正常。
防火墙和 SELinux 会静默丢弃 UDP 包,别只盯着代码
UDP 没有连接建立过程,发包失败或收不到响应,90% 不是代码问题,而是系统层拦截。常见表现:本地 nc -u 127.0.0.1 2345 能通,但局域网其他机器发不通。
- Linux:
iptables -L -n | grep :2345看是否被 DROP;临时放行:iptables -I INPUT -p udp --dport 2345 -j ACCEPT - CentOS/RHEL 8+ 默认启用 firewalld:
firewall-cmd --add-port=2345/udp --permanent && firewall-cmd --reload - SELinux 开启时,即使端口开放也可能拒绝 UDP 绑定,临时关闭测试:
setenforce 0
UDP 的“静默失败”特性决定了:只要外部收不到包,先查系统网络策略,再查代码。











