在Go中通过序列号、ACK确认、超时重传、滑动窗口及分片重组等机制,利用goroutine和channel构建高效可靠的UDP传输,适用于游戏、音视频等高实时性场景。

Go语言实现基于UDP的可靠传输层封装,核心不是替换UDP,而是用应用层逻辑补足它缺失的可靠性——序列号、ACK、重传、滑动窗口、分片重组,全部自己写。net.UDPConn只提供原始收发能力,没有连接状态、无序号、无超时、无重传,所谓“可靠UDP”本质是ARQ协议在UDP之上的模拟。
序列号与包头设计
每个UDP数据包头部必须显式编码定长字段,建议用大端字节序(binary.BigEndian)写入:
- seq:uint32,唯一递增,避免回绕歧义;溢出后从0继续,接收端靠滑动窗口判断是否重复
- ack:uint32,表示已连续收到的最大seq(即rcv_nxt − 1),用于快速确认累积接收进度
- conv:uint32,会话标识,防止不同连接的数据包互相干扰(尤其NAT环境下)
- flag:1字节,区分DATA/ACK/HEARTBEAT等类型,便于协议解析分流
ACK与重传控制
别用time.Sleep做重传等待——它无法中断,一卡就是整段延迟。正确做法是为每个待确认包分配独立*time.Timer:
- 发送DATA包时,启动定时器,超时触发重传;收到对应ack_seq立即调用timer.Stop()
- Timer.Reset()在已触发状态下返回false,需检查返回值,必要时新建Timer
- 初始RTO设为200ms,后续按指数退避(200 → 400 → 800),上限重传3–5次,失败即通知上层关闭会话
- 启用延迟ACK:收到2个包或20ms内无新数据再回ACK;但对乱序包、重复包必须立即响应,防止发送端误判丢包
滑动窗口与并发安全
接收端维护map[uint32][]byte缓存不连续包,只向上层交付从rcv_nxt开始的连续段;发送端用inflight map记录未确认包及对应timer。并发读写这些结构必须拆锁:
- 接收缓冲区用sync.RWMutex(读多写少,性能友好)
- 发送队列和inflight映射用独立sync.Mutex
- 重传goroutine在触发前先Lock()检查该seq是否已被ACK,确认后再Unlock(),绝不把WriteToUDP拖进临界区
- Packet结构体字段全用uint32/int64,避免指针,方便atomic操作更新时间戳或状态
分片、心跳与连接抽象
UDP单包受限于MTU(通常1400字节),大消息必须分片:
- 发送端按MTU切分,每片带msg_id、frag_id、total_frags;接收端用map[msg_id][]*Fragment + RWMutex缓存,超时未收齐则丢弃
- 定期发心跳包(含conv+timestamp),对方回ACK维持会话;连续3次无响应,触发连接失效清理
- 用struct封装虚拟连接(LocalAddr/RemoteAddr/State/Context),对外暴露类似net.Conn的Read/Write接口,屏蔽底层UDP细节
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











