Go如何实现UDP通信_Go UDP发送与接收说明
技术百科
P粉602998670
发布时间:2026-01-17
浏览: 次 UDP发送端必须设置写超时(如SetWriteDeadline),ReadFromUDP需校验返回长度并处理截断,多实例共用端口需手动启用SO_REUSEADDR,且UDP无连接状态,地址须每次显式传递。
UDP发送端必须设置超时,否则WriteToUDP可能永久阻塞
Go 的 *net.UDPConn 默认是阻塞模式,WriteToUDP 在网络异常(如目标主机不可达、路由中断)时不会立即失败,而是等待系统底层 ICMP 错误返回——这个过程可能长达数分钟。实际部署中极易导致 goroutine 积压。
- 务必在创建连接后调用
SetWriteDeadline或SetWriteTimeout - 推荐用
SetWriteDeadline(time.Now().Add(2 * time.Second)),而非固定超时值 - 错误检查不能只看
err != nil,要区分net.ErrWriteTimeout和真实网络错误
conn, _ := net.ListenUDP("udp", &net.UDPAddr{Port: 0})
defer conn.Close()
conn.SetWriteDeadline(time.Now().Add(2 * time.Second))
_, err := conn.WriteToUDP([]byte("ping"), &net.UDPAddr{IP: net.ParseIP("10.0.0.1"), Port: 8080})
if err != nil {
if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
log.Println("write timeout")
} else {
log.Printf("write failed: %v", err)
}
}
ReadFromUDP返回的n长度必须校验,UDP数据报可能被截断
UDP 是无连接、不可靠协议,内核接收缓冲区大小有限(通常 212992 字节 Linux 默认),当单个 UDP 包超过 MTU(一般 1500)且开启 IP 分片时,任意一片丢失都会导致整包丢弃;但更常见的是应用层读取缓冲区太小,ReadFromUDP 只写入前 len(buf) 字节并截断剩余内容,而不会报错。
- 接收缓冲区建议至少
65536字节(IPv4 最大 UDP 数据报理论值) - 必须检查返回的
n是否等于预期消息长度,或按协议自定义分隔符解析 - 不要假设
ReadFromUDP一次读完一个完整业务消息——UDP 本身不保证“消息边界”语义
buf := make([]byte, 65536)
for {
n, addr, err := conn.ReadFromUDP(buf)
if err != nil {
log.Printf("read error: %v", err)
continue
}
if n == 0 {
continue // 忽略空包
}
// 实际业务逻辑:解析 buf[:n]
handleMessage(buf[:n], addr)
}多个UDP服

SO_REUSEADDR,但Go默认不启用
Linux/macOS 下,若已有进程绑定 :8080,后续 ListenUDP 默认会报 address already in use。虽然 TCP 场景常用 SO_REUSEADDR 解决端口复用,但 Go 标准库的 net.ListenUDP 不暴露 socket 选项控制,必须通过 net.ListenConfig + Control 函数手动设置。
- 仅当明确需要多实例监听同一端口(如负载分散、热升级)时才启用
- Windows 不支持
SO_REUSEADDR用于 UDP 多播/单播混用场景,行为不一致 - 启用后仍需确保各实例处理逻辑互斥,避免重复消费
lc := net.ListenConfig{
Control: func(fd uintptr) {
syscall.SetsockoptInt32(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
},
}
l, err := lc.ListenPacket(context.Background(), "udp", ":8080")
if err != nil {
log.Fatal(err)
}
conn, _ := l.(*net.UDPConn)UDP没有连接状态,WriteToUDP和ReadFromUDP必须配对使用地址
与 TCP 不同,UDP 连接对象 *net.UDPConn 本身不维护远端地址。即使你用 net.DialUDP 创建了“已连接”的 conn,其内部也只是缓存了对端地址,WriteToUDP 仍允许传入不同地址(此时会忽略 conn 自带地址),而 ReadFromUDP 总是返回实际发包方地址。这意味着无法靠 conn 自身判断“谁发来的”或“该回给谁”,每次收发都必须显式处理 *net.UDPAddr。
- 不要依赖
DialUDP创建的 conn 做“单向通信”假设——它只是语法糖 - 服务器场景下,必须保存每次
ReadFromUDP返回的addr,再用它调用WriteToUDP回复 - 客户端若需并发请求多个地址,应复用同一
UDPConn,避免频繁创建销毁 fd
最容易被忽略的一点:UDP 没有“连接关闭”概念,Close() 只是释放本地 fd,对端完全无感知。因此心跳、重连、会话超时等逻辑全部要应用层自己实现。
# ai
# 的是
# 多个
# windows
# 而不
# 已有
# 片时
# 自定义
# 可达
# 复用
# mac
# win
# linux
# 端口
# udp
# go
# 路由
# 并发
# 对象
# macos
# cos
# 字节
# 标准库
# nil
# 并发请求
# len
# 会报
# 应用层
相关栏目:
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
AI推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
SEO优化<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
技术百科<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
谷歌推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
百度推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
网络营销<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
案例网站<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
精选文章<?muma echo $count; ?>
】
相关推荐
- Windows10如何删除恢复分区_Win10 D
- 如何关闭Win10自动更新更新_Win10系统自动
- C#怎么使用委托和事件 C# delegate与e
- php8.4匿名类怎么用_php8.4匿名类创建与
- c++输入输出流 c++ cin与cout格式化输
- 如何在 Go 中调用动态链接库(.so)中的函数
- php下载安装后memory_limit怎么设置_
- 如何诊断并终止卡死的 multiprocessin
- Win11怎么设置屏保时间_调整Win11屏幕保护
- 如何高效识别并拦截拼接式恶意域名 spam
- Win11怎么关闭定位服务 Win11禁止应用获取
- 使用类变量定义字符串常量时如何实现类型安全的 Li
- c# 在高并发场景下,委托和接口调用的性能对比
- Win10路由器怎么隐藏ssid Win10隐藏w
- LINUX怎么查看进程_LINUX ps命令查看运
- Python解释执行模型_字节码流程说明【指导】
- Windows10怎样设置家长控制_Windows
- Win11无法拖拽文件到任务栏怎么办_Win11开
- php订单日志怎么导出excel_php导出订单日
- php订单日志怎么按状态筛选_php筛选不同状态订
- VSC怎么在PHP中调试MySQL_数据库交互排查
- Win11怎么退出微软账户_切换Win11为本地账
- Win10怎么更改用户名 Win10修改账户名称操
- Win11怎么把图标拖到任务栏_Win11固定应用
- Win11怎么设置ipv4地址_Windows 1
- Win11怎么关闭自动修复_跳过Win11开机自动
- Linux怎么修改用户密码_Linux系统pass
- Windows系统被恶意软件破坏后的恢复策略_错误
- Win10如何关闭安全中心所有通知 Win10禁用
- 如何在 IIS 上为 ASP.NET 6 应用排除
- Win11怎么开启游戏模式_Win11优化游戏帧数
- Win11时间不对怎么同步_Win11自动校准互联
- Win10怎样卸载DockerDesktop_Wi
- 如何用正则表达式精确匹配“start”到“end”
- Windows怎样关闭锁屏广告_Windows关闭
- Win11怎么设置默认图片查看器_Windows1
- C++中的constexpr和const有什么区别
- 如何在Golang中指定模块版本_使用go.mod
- Bpmn 2.0的XML文件怎么画流程图
- MAC如何隐藏文件夹及文件_MAC终端命令隐藏与第
- PhpStorm怎么调试PHP代码_PhpStor
- LINUX的SELinux是什么_详解LINUX强
- Win11怎么关闭任务栏小组件_Windows11
- mac怎么分屏_MAC双屏显示与分屏操作技巧【指南
- Win11色盲模式怎么开_Win11屏幕颜色滤镜设
- ACF 教程:如何正确更新嵌套在多层 Group
- Mac如何使用听写功能_Mac语音输入打字【效率技
- Win11声音太小怎么办_Windows 11开启
- Windows11怎样开启游戏模式_Windows
- mac怎么看硬盘大小_MAC查看磁盘存储空间与文件

QQ客服