必须用 encoding/binary 手动解析二进制协议:严格匹配字节序(BigEndian/LittleEndian)、使用定长类型(如 uint32)、字段首字母大写、结构体字段顺序与协议一致、先处理 TCP 粘包(读固定头→提取长度→io.ReadFull 读完整包)、变长字段需分步读取长度前缀和内容。

直接用 encoding/json 解析二进制协议一定会报 invalid character 错误——它根本不是 JSON,连字节流开头都不是 `{`,而是裸的 magic number 或长度字段。必须用 encoding/binary 手动按协议定义逐字段拆解。
binary.Read 为什么一读就得到 0 或极大随机数
几乎全是字节序传错。Go 不做任何猜测或兼容:你传 binary.LittleEndian,它就按小端解;协议实际是大端(比如抓包看到前 4 字节是 00 00 01 00),uint32 就会解成 65536 而不是 256。
- 抓包看原始 hex:
00 00 00 01→ 对应uint32 = 1→ 协议用大端 → 必须传binary.BigEndian -
01 00 00 00→ 小端 → 必须用binary.LittleEndian - Python 的
struct.pack('iih')默认小端,Go 端必须严格匹配,别靠“试”,查协议文档或抓包确认
结构体字段导出 + 定长类型 + 字节序对齐缺一不可
binary.Read 不猜、不兼容、不自动转换。它严格按你传的 order 和结构体定义来解码,错一点就全错。
- 字段名首字母小写(如
id int32)会被完全跳过,值保持零值,不报错也不警告 - 禁用
int/uint:它们在 32 位和 64 位平台长度不同;协议里写死的是int32或uint16,你就得用对应定长类型 -
string或[]byte字段会直接 panic:"invalid type";必须改用[32]byte这类定长数组 - 字段顺序必须和协议文档一字不差,错一位,后续全偏移
-
binary.Size(&s)可用来校验 struct 实际大小是否和协议一致
TCP 粘包必须在 binary.Read 之前解决
把 net.Conn 直接丢给 binary.Read,等于让解析器在流动字节流里“盲猜”包边界。常见错误包括魔数读成 0、长度字段被截半、io.ReadFull 死等。
立即学习“go语言免费学习笔记(深入)”;
- 先读协议头固定长度(如前 4 字节),用
binary.Read(r, order, &pkgLen)提取完整包长 - 立刻校验
pkgLen是否合理(如MB且>= 最小合法包长),超限直接断连 - 调用
io.ReadFull(r, data[:pkgLen])(不是Read)确保读满 - 拿到完整
data后,再用bytes.NewReader(data)交给binary.Read - 别信“协议文档说包长固定”——TCP 层不认这个,每条连接都可能粘包
变长字段必须手动拆解,不能依赖 binary.Read
binary.Read 对 []byte、string、map、interface{} 零支持。协议里任何非固定长度字段(用户名、JSON 载荷、TLV 子项),都必须拆成“长度前缀 + 内容”两步走。
- 先读定长头部,拿到长度字段值(如
nameLen uint16) - 再根据该值,用
io.ReadFull或binary.Read读对应长度的字节切片 - 别用
ReadString或ReadBytes,它们依赖分隔符,而二进制里没有'\0'或'\n'可靠分界
真正难的不是写对第一个 binary.Read,而是所有字段类型、字节序、内存对齐、粘包处理、变长字段边界检查全部对齐——漏掉任意一环,数据就 silently 错位,调试时很难定位到哪一步开始偏了。


















