binary.read必须配合长度前缀和io.readfull才能安全使用:先读4字节包长并校验,再用io.readfull读满整包,经bytes.newreader封装后解析;结构体字段须导出、定长、字节序对齐,禁用string/[]byte。

binary.Read 必须配合长度前缀和 io.ReadFull 才能用
直接把 net.Conn 丢给 binary.Read 几乎必然失败,因为 TCP 是字节流,没有包边界。你看到的“读不出数据”“数值错乱”“io.ErrUnexpectedEOF”,90% 是没做分包导致的。
- 先读固定长度头(如 4 字节
uint32),用io.ReadFull确保读满,再用binary.BigEndian.Uint32()解出 payload 长度 - 根据该长度分配切片,再次用
io.ReadFull读取完整 payload,**绝不用conn.Read()** - 拿到完整字节切片后,再用
bytes.NewReader(payload)包装,传给binary.Read解析内部结构 - 必须校验
pkgLen是否超过上限(如 1MB),否则恶意客户端可触发内存暴涨
结构体字段必须导出、定宽、字节序严格对齐协议
binary.Read 不认小写字段、不猜字节序、不兼容平台相关类型——它只做字节搬运,错一点就全错。
- 所有字段名首字母必须大写(如
Magic,不能是magic),否则被跳过且不报错 - 禁用
int/uint,改用int32、uint16等明确长度的类型 - 抓包看原始 hex:若前 4 字节是
00 00 01 00→ 对应uint32 = 256→ 协议是大端;反之00 01 00 00就是小端 - 结构体字段顺序必须和协议文档一字不差,
binary:tag 完全无效,别写
变长字段(字符串、切片)不能塞进 struct 里直读
binary.Read 遇到 []byte、string、map 会直接 panic:invalid type。这不是 bug,是设计限制。
- 字符串字段必须定义为定长数组,例如
Name [32]byte,读完后用bytes.TrimRight(name[:], "\x00")去零再转string() - 真要支持 UTF-8 变长名?协议里必须带长度前缀:先读
uint16 nameLen,再调io.ReadFull(r, buf[:nameLen]) - 别把整个 struct 传给
binary.Write—— 尤其含 slice 字段时,手动分字段写更可控、更安全 - 如果协议有校验和(如 CRC32),应在提取 payload 后立即验证,失败则丢弃整帧
Colly 或 gRPC 等框架里注册自定义解析器不是加个函数就行
框架只负责调用,真正解析逻辑仍得你手写。注册只是绑定 MIME 类型或协议标识到具体实现,不解决底层字节解析问题。
- Colly 的
RegisterParser("application/custom", &MyParser{})要求MyParser实现Parse(resp *http.Response, c *Collector) error,里面仍需手动读 body、校验魔数、分帧、调binary.Read - gRPC 自定义编解码器(
encoding.RegisterCodec)也一样:你提供的Marshal/Unmarshal方法,最终还是要处理长度头 +binary.Read这套流程 - 别指望框架自动识别“这是我的二进制协议”,它连 magic number 都不会帮你比对——这事得你写在
Parse或Unmarshal开头
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











