net/http 不适合可靠大文件传输,因其缺乏断点续传、校验机制,易受路径遍历、并发写冲突及超时中断影响;应基于 net.Conn 自建二进制协议,用 bufio.Writer 控制写入节奏、io.CopyBuffer 复用缓冲、binary.Read/binary.Write 管理长度头、io.ReadFull 保证读取完整。

为什么 net/http 不适合做可靠大文件传输
直接用 http.ServeFile 或 http.FileServer 暴露文件,看似简单,但实际会遇到断点续传缺失、无校验、并发写冲突、路径遍历漏洞等问题。尤其当传输 >100MB 的文件时,HTTP 默认的超时和缓冲机制容易导致连接中断且无法恢复。
真正可控的传输应自己管理连接生命周期、分块逻辑和错误重试。推荐基于 net.Conn 构建二进制协议,而非复用 HTTP 语义。
如何用 io.Copy + bufio.Writer 实现稳定发送
发送端核心是控制写入节奏和确保数据落盘。不要直接 conn.Write([]byte) 大块数据,它可能阻塞或截断。
- 用
bufio.NewWriterSize(conn, 64*1024)包装连接,缓冲区设为 64KB 避免小包泛滥 - 发送前先写 8 字节长度头(
binary.Write(w, binary.BigEndian, int64(len(data)))),接收方据此预分配 - 调用
io.Copy时传入带缓冲的Writer,而非裸conn;结束后必须w.Flush() - 若文件极大,改用
io.CopyBuffer并复用[]byte缓冲池,避免 GC 压力
接收端如何防止粘包和读取不全
TCP 是字节流,conn.Read 可能返回任意长度数据,不能假设一次读完一个“消息”。必须自己解析边界。
立即学习“go语言免费学习笔记(深入)”;
- 先读固定 8 字节头,用
binary.Read(r, binary.BigEndian, &fileSize)解出后续数据长度 - 再循环调用
io.ReadFull(r, buf)直到读满fileSize,它会自动重试未读完部分 - 绝对不要用
conn.Read(buf)后检查n 来判断结束——这在 TCP 中毫无意义 - 接收文件时用
os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644),避免并发写覆盖
怎么加基础校验和连接保活
没有校验的传输等于没传。保活不是可选,而是防止中间设备(如 NAT 网关)静默断连的关键。
- 发送前计算文件 SHA256,作为元数据随长度头一起发(例如:头结构为
[8B len][32B sha256]) - 接收端边写入边更新
hash.Hash,写完比对,不一致则删临时文件并返回错误码 - 空闲连接每 30 秒发一个单字节心跳(如
0xFF),接收端超时 45 秒未收心跳即关闭连接 - 所有
Read/Write操作都设conn.SetDeadline(time.Now().Add(5 * time.Minute)),避免永久阻塞
校验和保活逻辑看着琐碎,但漏掉任意一项,工具在真实网络(尤其是跨运营商、带 Wi-Fi 中继)中就会间歇性失败,且难以定位。










