Workerman中UDP的send()必须显式传入IP和端口,$connection->send($data, $ip, $port)是唯一安全用法;onMessage中可通过getRemoteIp()/getRemotePort()获取来源地址用于回包;$worker->connections不可靠,广播需自行维护地址列表。

send() 在 Workerman 的 UDP 场景下,不是连接对象的方法,而是 UdpConnection 实例的直接方法 —— 但它只对「已知目标地址」的连接有效;UDP 本身无连接,所以这个“连接”其实是 Workerman 封装的一次性通信上下文,send() 发送时必须携带目标 IP 和端口。
UDP send() 必须传入完整地址信息
Workerman 的 UdpConnection 对象不维护远端地址(不像 TCP 那样有固定 peer),每次调用 send() 都需显式指定目标地址。否则会报错:Warning: socket_sendto(): unable to write to socket 或静默失败。
-
$connection->send($data, $ip, $port)是唯一安全用法,$ip和$port缺一不可 - 不能写成
$connection->send($data)—— 这在 UDP Worker 中无效,也不会抛异常,但数据根本发不出去 - 如果想复用地址,建议把
$ip和$port存在$connection->address或自定义属性里,比如$connection->target = ['192.168.1.100', 8080]
onMessage 回调里的 $connection 已带来源地址
当客户端发来 UDP 包,onMessage 触发时,$connection 对象已隐含来源信息,可通过 $connection->getRemoteIp() 和 $connection->getRemotePort() 获取。这是你做「回包」或「单播响应」的依据。
- 常见误操作:直接
$connection->send($response)→ 不会发出去 - 正确做法:
$connection->send($response, $connection->getRemoteIp(), $connection->getRemotePort()) - 注意:
getRemoteIp()在多进程 + NAT 环境下可能返回内网地址(如 127.0.0.1),需结合业务场景判断是否可信
广播给多个客户端要自己遍历 connections
UDP 没有原生广播机制(不像 TCP 可以 foreach 所有 $worker->connections 然后 send()),因为每个 UdpConnection 不代表一个长连接,而是一个临时通信句柄。Workerman 的 $worker->connections 在 UDP 模式下仅记录当前活跃的「最近通信过的客户端地址」,且默认 60 秒无活动自动清理。
- 若需群发,必须自己维护一个地址列表(如 Redis Set 或 PHP 数组),在
onMessage中存入[$ip, $port] - 发送时循环该列表,逐个调用
$connection->send($data, $ip, $port) - 不要依赖
$worker->connections做广播 —— 它不稳定,尤其在高并发或心跳间隔长时,容易漏掉客户端
send() 失败不抛异常,但可检查返回值
send() 返回 int 类型:成功时返回发送字节数,失败返回 false。PHP 的 UDP socket 层本身不保证送达,所以失败通常意味着系统缓冲区满、目标不可达或权限问题(如非 root 进程绑定低端口)。
- 建议加简单判断:
if ($connection->send($data, $ip, $port) === false) { error_log("UDP send failed to $ip:$port"); } - 不推荐重试 —— UDP 重发违背协议语义,且可能加剧网络拥塞;应由上层协议(如自定义 ACK)处理可靠性
- 若频繁失败,优先查
netstat -su看 UDP 接收错误,或用ss -uln确认端口监听状态
真正容易被忽略的是:UDP 的 send() 行为完全取决于内核 socket 状态和路由表,Workerman 不做地址解析、不缓存 DNS、也不重试 —— 它只是把数据交出去。所以线上出问题时,先确认目标地址是否可达,再看 PHP 层逻辑。

















