UDP服务端必须用net.ListenUDP而非net.Listen,因后者仅支持TCP等面向连接协议;UDP无连接,需用ReadFromUDP/WriteToUDP配对操作,地址需解析为*net.UDPAddr,超时靠SetDeadline控制。

UDP服务端监听必须用 net.ListenUDP,不能用 net.Listen
Go 的 net 包里,net.Listen 只支持面向连接的协议(比如 TCP),直接传 "udp" 会 panic:「protocol not available」。UDP 是无连接的,必须用专门的 net.ListenUDP。
常见错误是照搬 TCP 写法,写成 net.Listen("udp", ":8080") —— 这行不通,运行时直接崩溃。
-
net.ListenUDP第一个参数是*net.UDPAddr,通常用net.ResolveUDPAddr("udp", ":8080")构造 - 返回的
*net.UDPConn支持ReadFromUDP和WriteToUDP,不是Read/Write - 注意地址复用:Linux/macOS 默认允许端口重用(
SO_REUSEADDR),但 Windows 需显式设置SetReadBuffer前调用SetDeadline才能避免 “address already in use”
客户端发包前得先解析目标地址,net.DialUDP 不是必须的
很多人以为 UDP 客户端必须先 DialUDP,其实不是。UDP 本质无连接,net.DialUDP 只是帮你封装了一个绑定本地地址 + 记住远端地址的 *net.UDPConn,方便反复 Write。但更常见、更轻量的做法是:用 net.ListenUDP 绑定本地(甚至可设为 nil 让系统自动选端口),再用 WriteToUDP 直接发给任意目标。
典型场景:日志上报、监控打点、广播探测——你根本不需要维持“连接状态”,也不关心对方是否在线。
立即学习“go语言免费学习笔记(深入)”;
- 如果只是单次发包,用
net.ListenUDP("udp", nil)+WriteToUDP最简单 -
net.DialUDP适合需要固定本地端口、或想复用同一 socket 多次发往同一目标的情况 - 目标地址必须是
*net.UDPAddr,不能传字符串;net.ResolveUDPAddr("udp", "127.0.0.1:9999")必不可少
读写必须配对用 ReadFromUDP / WriteToUDP,别混用 Read/Write
*net.UDPConn 实现了 io.Reader 和 io.Writer 接口,但直接调 Read 或 Write 会 panic:「use of closed network connection」或「invalid argument」。因为 UDP 数据包自带源地址,ReadFromUDP 才能拿到 sender 的 IP 和 port,WriteToUDP 才能指定发给谁。
这个坑特别隐蔽:代码能编译,一跑就挂,错误信息还不直观。
- 服务端收包必须用
conn.ReadFromUDP(buf),返回n, addr, err - 回包必须用
conn.WriteToUDP(resp, addr),否则不知道发给谁 - buffer 长度建议 ≥ 65535(UDP 最大理论包长),但实际常用 1500–8192,太小会截断
- 记得检查
n,不是所有字节都有效;err可能是io.EOF(仅在 conn 关闭时)或网络错误
超时控制只能靠 SetDeadline,没有内置重试或确认机制
UDP 本身不保证送达、不重传、不排序。Go 的 *net.UDPConn 也没提供类似 TCP 的 SetReadTimeout。唯一可控的是 SetDeadline(绝对时间)或 SetReadDeadline/SetWriteDeadline(相对时间),超时后操作返回 timeout 错误。
这意味着:你要自己实现丢包检测、重发逻辑、应用层 ACK,或者接受“尽力而为”。别指望 runtime 替你兜底。
- 服务端一般设
SetReadDeadline(time.Now().Add(30 * time.Second))防卡死 - 客户端发完想等响应?得自己
ReadFromUDP并配超时,然后比对响应内容和事务 ID - 广播场景(
255.255.255.255或子网广播地址)下,SetWriteDeadline无效,系统可能静默丢包
UDP 简单,但“无连接”三个字背后全是取舍:没握手开销,也就没了任何连接上下文。每次读写都要自己管地址、自己判超时、自己容错。写顺手了很轻快,写漏一点,问题就藏在深夜的偶发丢包里。










