UDP服务器用net.ListenUDP监听,必须用ReadFrom获取客户端地址后WriteTo回发;需手动处理空包、丢包、并发安全及缓冲区大小,无内置连接状态管理。

UDP服务器怎么监听并收发数据
Go 的 net.ListenUDP 是唯一入口,它返回一个 *UDPConn,后续所有读写都基于它。别用 net.Listen 或 HTTP 服务那一套——UDP 没连接概念,不握手、不保序、不重传。
常见错误是直接对 *UDPAddr 调 WriteTo 却没先调 ReadFrom 获取对方地址,结果发出去没人收;或者用 conn.Write(这是 TCP 才有的方法),编译报错 UDPConn.Write undefined。
- 监听必须指定具体地址,
":8080"可以,"localhost:8080"也可以,但"127.0.0.1:8080"和"0.0.0.0:8080"行为不同:后者能收外网包,前者只收本机回环 - 读数据必须用
ReadFrom,它返回n, addr, err,其中addr是客户端真实地址,下一次WriteTo必须用它,不能复用监听时的*UDPAddr - 缓冲区大小自己管:传入的
[]byte太小会截断包,UDP 包最大理论值是 65507 字节,但实际建议不超过 1400(避开 IP 分片)
怎么处理并发读写和连接状态
UDP 本身无连接,但业务常需要“逻辑会话”,比如游戏客户端心跳、IoT 设备上报。Go 不提供内置 session 管理,得自己维护映射表,且必须加锁——sync.Map 比 map + mutex 更适合高频读写场景。
容易踩的坑是把 ReadFrom 放在 for 循环里阻塞等待,却没开 goroutine,导致整个服务器卡死;或多个 goroutine 并发写同一个 *UDPConn,引发 panic:write udp4 127.0.0.1:8080: use of closed network connection(其实是并发 close 或 write 冲突)。
立即学习“go语言免费学习笔记(深入)”;
- 每个
ReadFrom调用应包裹在独立 goroutine 中,典型模式是for { go handlePacket(conn) } -
WriteTo是线程安全的,但如果你在写之前查了某个设备状态又更新了它,就得自己加锁,*UDPConn不负责业务一致性 - 不要依赖
conn.RemoteAddr()——UDP 连接对象没有远端地址,它永远是nil,必须靠每次ReadFrom返回的addr
怎么应对丢包、乱序和空包
UDP 不保证送达,Go 的 ReadFrom 返回 n == 0 且 err == nil 就是收到了空包(比如客户端 sendto 传了零长度 slice),这不算错误,但业务层要判断过滤,否则可能触发 panic 或逻辑错乱。
丢包和乱序无法靠 Go 标准库解决,得自己加序列号、ACK、超时重传。如果只是做日志上报或监控指标采集,可以接受丢包,但千万别在 ReadFrom 后不做 if n == 0 { continue } 就直接 json.Unmarshal,会解出空结构体或 panic。
- 收到包后第一件事检查
n > 0,否则跳过处理 - 用
time.Now().UnixNano()记录接收时间,比依赖系统时钟更稳;但别在高并发场景下频繁调,可考虑每批包共享一个时间戳 - 内核 UDP 接收缓冲区默认很小(Linux 通常 212992 字节),突发流量会丢包,可通过
sysctl -w net.core.rmem_max=4194304调大,Go 代码里也能用conn.SetReadBuffer设置,但不能超过系统上限
怎么测试本地 UDP 服务是否正常工作
别一上来就写客户端代码,先用系统命令验证通路。Linux/macOS 用 nc -u 127.0.0.1 8080,输入文字回车就发;Windows 用 PowerShell -Command "[System.Net.Sockets.UdpClient]::new('127.0.0.1', 8080).Send([Text.Encoding]::UTF8.GetBytes('test'), 4)"。
常见失败现象:命令没反应、提示 Connection refused(端口没监听)、或服务器没打印日志(但命令显示已发送)。这时候优先检查 Go 程序是否真在运行、监听地址是否绑定了 0.0.0.0 而不是 127.0.0.1(跨容器/网络时尤其关键)。
- Go 启动后立刻用
lsof -i :8080或netstat -an | grep 8080确认监听状态,看 Proto 是否为udp,State 是否为空(UDP 没 state) - 防火墙可能拦截 UDP:macOS 的
pf、Linux 的iptables、Windows Defender 都可能默认禁 UDP 入站 - 用
tcpdump -i lo0 udp port 8080抓包,能看到“发出去了但没回来”,说明问题在服务端逻辑而非网络层
UDP 的麻烦不在 Go 语法,而在你得亲手补全 TCP 默认提供的那层抽象:连接管理、流控、重试、粘包处理。哪怕只是 echo 服务,也要想清楚——空包怎么跳、大包怎么截、并发写怎么不崩、客户端断连了要不要清状态。这些都不是 net 包替你决定的。


















