UdpConnection的send()是UDP通信唯一出口,无需握手;onMessage中直接调用即可回发数据,因$connection已含客户端地址;多进程下地址可能丢失,建议单进程或心跳保活。

UdpConnection 对象的 send() 方法就是返回数据的唯一出口,不需要额外握手或连接管理。
onMessage 回调里直接用 $connection->send()
Workerman 的 UDP 通信模型是“收即发”,服务器拿到数据包后,$connection 已经携带了客户端的地址和端口(由内核在 recvfrom() 时填充),所以你只需调用 send(),框架会自动把响应发回原客户端。
-
$connection是UdpConnection实例,不是 TCP 连接,不维护状态 -
$data是原始二进制字符串,send()也只接受字符串,不自动序列化 - 如果返回 JSON,得自己
json_encode();返回 Protobuf,得先serialize()或编码
示例:
$udpWorker->onMessage = function(UdpConnection $connection, $data) {
// 简单回显
$connection->send('ACK: ' . $data);
<pre class="brush:php;toolbar:false;">// 或结构化响应
$response = json_encode(['status' => 'ok', 'received' => strlen($data)]);
$connection->send($response);};
多进程下 stream_socket 导致的地址丢失问题
当 $udpWorker->count > 1 时,Workerman 默认改用 stream_socket_server() + stream_select() 模式,此时首次收到包后,$connection->getRemoteAddress() 可能失效,后续 send() 会失败并报错 PHP Warning: sendto(): unable to write to socket。
- 根本原因是:多进程模式下,多个子进程共享同一个 socket,但内核只把第一次
recvfrom()的对端地址缓存在当前进程上下文,后续sendto()若落在其他进程,地址信息就没了 - Workerman 4.0+ 在多进程下已尝试做地址缓存,但仍有约 5 分钟保活窗口(Linux conntrack 超时),超时后地址丢失,
send()静默失败
应对方式:
- 强制单进程:
$udpWorker->count = 1,用原生socketAPI,地址全程可靠 - 或改用心跳保活:客户端每 60 秒至少发一个空包,维持内核会话状态
- 不要依赖
$connection->getRemoteAddress()做二次路由,它在多进程下不稳定
不要用 sendto() 手动操作底层 socket
有人试图绕过 $connection->send(),直接调用 PHP 原生 socket_sendto(),这会导致:
- 与 Workerman 的事件循环冲突,可能卡住
EventLoop - 地址结构需手动构造(
sockaddr_in),容易出错 - 绕过框架的错误处理和缓冲区管理,丢包或阻塞风险上升
Workerman 的 UdpConnection::send() 已封装好地址复用逻辑,只要确保 $connection 来自 onMessage 回调,就足够安全。
UDP 返回数据这件事本身很简单,真正复杂的是地址生命周期管理——尤其在多进程部署时,你以为发出去了,其实可能根本没发出去,而且还不报错。别被“无连接”三个字骗了,Linux 内核对每个 UDP 五元组是有会话缓存的,这个缓存不是永久的。











