net.Conn.Read 不保证一次读完完整消息,因 TCP 是字节流、无消息边界;需自行定义分界(如换行符或长度头),配合 bufio.NewReader 或分步读取处理。

net.Conn.Read 读不到完整消息?因为 TCP 是字节流,不是消息边界
Go 的 net.Conn.Read 不保证一次读完你期待的“一条消息”,它只按底层 TCP 缓冲区当前可用字节数返回。比如客户端发了 "hello\nworld\n",服务端调用 Read 可能只拿到 "hel"、"lo\nwor" 或整段——这取决于网络延迟、内核缓冲区状态和写入方是否 flush。
常见错误现象:Read 返回 n=5,但后续再读才拿到剩下部分,业务逻辑误以为消息已结束;或反复读到空字符串却没检查 err == io.EOF。
- 不要依赖单次
Read获取完整业务消息,必须自己定义分界(如换行符、固定头长、TLV) - 用
bufio.NewReader配合ReadString('\n')或ReadBytes('\n')处理行协议,比裸Read更可靠 - 若协议含长度头(如前 4 字节表示 body 长度),需先读够头,再按长度读 body,不能跳过中间步骤
为什么 conn.Write([]byte) 看似成功,但对方收不到?
Write 返回 n, nil 只代表数据已拷贝进 Go 的 socket 发送缓冲区,并不等于对方已收到。TCP 的 ACK 延迟、Nagle 算法、中间设备丢包都可能导致数据滞留或丢失。
使用场景:短连接中容易忽略这点;长连接上频繁小包写入时更明显(例如每秒发 10 条 10 字节消息)。
立即学习“go语言免费学习笔记(深入)”;
- 对可靠性要求高的场景,应在协议层加响应确认(如对方回
"ACK"),不能仅靠Write无错就认为送达 - 避免高频小包,可启用
SetNoDelay(true)关闭 Nagle 算法(conn.(*net.TCPConn).SetNoDelay(true)),减少延迟但增加包数量 - 写入后立即
conn.Close()可能导致缓冲区未发完就被 RST,应先shutdown或等对方 ACK(Go 中无直接 shutdown,需靠应用层约定)
序列化结构体时,json.Marshal 和 binary.Write 怎么选?
JSON 适合跨语言调试和人类可读场景,但体积大、解析慢;binary.Write(或 encoding/binary 手动编排)紧凑高效,但强耦合 Go 类型且不可读。
性能影响:1KB 结构体 JSON 序列化约耗时 20–50μs,binary.Write 通常
- 内部微服务间通信、高频 IoT 数据上报,优先用二进制格式(如
gob或自定义binary协议) - 对外 API、配置下发、调试日志,用 JSON 并开启
json.Encoder.SetEscapeHTML(false)省点开销 - 注意
gob不兼容不同 Go 版本或非 Go 客户端,binary要严格对齐 struct 字段顺序和大小(int32必须是 4 字节)
UDP 场景下,ReadFromUDP 和 WriteToUDP 的地址参数容易漏什么?
ReadFromUDP 返回的 clientAddr 是对方真实地址,必须原样传给 WriteToUDP 才能回包;若误用 listener.LocalAddr() 或硬编码 "127.0.0.1:12345",包会发向错误目标甚至本地回环。
常见错误现象:服务端能收包,但客户端永远收不到响应;Wireshark 抓包发现响应目的 IP 是 127.0.0.1 而非客户端公网 IP。
-
WriteToUDP的地址参数必须是ReadFromUDP返回的net.Addr,不可转换为 string 再解析(可能丢失 IPv6 scope ID) - UDP 无连接,每次读写都要带地址;TCP 的
conn.Write隐式绑定已建立连接,这是根本差异 - 若需广播或多播,得用
net.ListenMulticastUDP,普通ListenUDP不支持


















