Go的net.Conn只处理字节流,故conn.Write(struct)必报错;必须先序列化为[]byte,再添加长度前缀(如4字节大端长度头)以解决TCP粘包问题。
Go 的
本身不认“消息”,只认字节流;你直接
必然报错
。真正能上线的自定义协议,必须同时解决两件事:序列化 + 消息边界。缺一不可。
为什么 conn.Write(struct) 一定失败
因为
接口只实现了
,而 Go struct 没有隐式转
的机制。常见错误写法包括:
直接传 struct:
→ 编译报错
用
拼字符串再转
→ 字段顺序、空值、时间格式全不可控
用
后直接
→ 解决了序列化,但没解决粘包,接收方读不到完整 JSON
核心原则:先
成
,再按协议规则封装(比如加长度头),最后
。
怎么加长度前缀解决 TCP 粘包
最简可靠方案是「4 字节大端长度 + payload」。这不是可选项,而是 TCP 流式特性的硬性要求。接收方必须先读够 4 字节,才知道接下来该读多少。
立即学习
“
go语言免费学习笔记(深入)
”;
go语言参考手册 中文CHM版
Go 是一个开源的编程语言,它能让构造简单、可靠且高效的软件变得容易。本文给大家带来Go参考手册,需要的可以来下载! Go是从2007年末由Robert Griesemer, Rob Pike, Ken Thompson主持开发,后来还加入了Ian Lance Taylor, Russ Cox等人,并最终于2009年11月开源,在2012年早些时候发布了Go 1稳定版本。现在Go的开发已经是完全开放的,并且拥有一个活跃的社区。 Go 语言特色 简洁、快速、安全 并行、有趣、开源 内存管理、v数组安全、编译
下载
写包用
,别用
—— 跨平台位宽不一致
读包必须用
,不能用裸
,否则可能只读到 2 字节就返回
长度字段本身不能压缩或加密,否则无法预判后续读取量
建议设上限(如
),防 OOM
示例解包逻辑:
选什么序列化格式才不至于线上翻车
别用
做跨语言通信,也别无脑上
就以为万事大吉。
是 Go 专属,Python/PHP 客户端根本解不开;Go 版本升级或结构体字段重排,旧客户端直接
在线上常因字段大小写、
默认序列化格式、浮点精度、
vs
处理模糊而失败,典型错误:
生产环境推荐
(强 schema、多语言、版本兼容)或
(轻量、多语言,但需自行约定 schema 演进)
如果只是内部服务且纯 Go,
可用,但必须保证服务端/客户端 Go 版本一致,且结构体字段顺序、导出状态严格一致
解包时 goroutine 卡死或泄漏的常见原因
不是协议写得不对,而是资源控制没跟上。
没设
→ 网络异常时
永久阻塞,goroutine 泄漏
粘包处理没剥离剩余字节 → 一次读到两个包,第二个包的开头被当成了新包的长度头,解析失败后无限等待
每次解包都
→ 小包高频分配触发 GC,吞吐掉一半
错误长度(如超 1GB)没立即断连 →
会等超时,期间占用连接和 goroutine
真正健壮的解包函数应返回
:第一个是完整消息,第二个是本次读取中未消费的剩余字节,用于下一次迭代 —— 这才是处理粘包的正解。
net.Connconn.Write(myStruct)cannot use myStruct (type MyStruct) as type []bytenet.ConnWrite([]byte)[]byteconn.Write(myData)fmt.Sprintf("%v", myData)[]bytejson.Marshal(myData)Write()Marshal[]byteWrite()binary.BigEndian.PutUint32(header, uint32(len(payload)))intio.ReadFull(conn, header[:])conn.Read()if length > 1024 * 1024 { return nil, ErrTooLarge }func readPacket(conn net.Conn) ([]byte, error) {
var lengthBuf [4]byte
if _, err := io.ReadFull(conn, lengthBuf[:]); err != nil {
return nil, err
}
length := binary.BigEndian.Uint32(lengthBuf[:])
payload := make([]byte, length)
if _, err := io.ReadFull(conn, payload); err != nil {
return nil, err
}
return payload, nil
}gobjsongobpanicjsontime.Timenil""json: cannot unmarshal string into Go struct field X.Timeprotobufmsgpackgobconn.SetReadDeadline()ReadFull()make([]byte, length)ReadFull([]byte, []byte, error)