因为二进制协议是无结构的字节序列,非json文本格式,encoding/json会报invalid character错误;必须用encoding/binary按字节偏移、大小端和类型手动解析,struct字段需导出、定宽、显式指定字节序。

为什么不能直接用 encoding/json 解析二进制协议
因为二进制协议不是文本格式,它没有字段名、括号或分隔符,只有按约定排布的字节序列。用 json.Unmarshal 会直接报 invalid character 错误——它根本读不到合法 JSON 结构。你得自己按字节偏移、类型长度、大小端规则一一手动拆解。
用 binary.Read 搭配 struct 是最稳的起点
Go 标准库的 binary.Read 支持把字节流按指定顺序和大小端写入结构体字段,前提是结构体字段有明确的字节对齐和类型映射。注意:必须用 binary.BigEndian 或 binary.LittleEndian 显式指定,不能省略。
常见错误包括:
- 结构体字段没加
padding导致内存对齐错位(比如中间插一个uint8后跟uint32,编译器可能自动补 3 字节) - 用了
int这种平台相关类型(32 位或 64 位不固定),应改用int32/int64 - 忽略协议里“保留字段”或“校验和”,导致后续字段全部偏移
示例协议头(4 字节 magic + 2 字节 version + 4 字节 length):
type Header struct {
Magic uint32
Ver uint16
Length uint32
}
buf := bytes.NewReader([]byte{0x42, 0x45, 0x45, 0x46, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0a})
var h Header
err := binary.Read(buf, binary.BigEndian, &h) // 注意取地址 &h
// h.Magic == 0x42454546, h.Ver == 1, h.Length == 10
遇到变长字段(如字符串、数组)怎么办
二进制协议里很少直接存字符串,通常先存长度(uint16 或 uint32),再存内容字节。这时候不能靠 binary.Read 一次性读结构体,得拆成两步:
- 先读定长头部,拿到长度字段值
- 再根据该值,用
io.ReadFull或binary.Read读对应长度的字节切片
别用 ReadString 或 ReadBytes,它们依赖分隔符,而二进制里没有 '\0' 或 '\n' 可靠分界。
例如头部后紧跟一个 uint16 表示 name 长度,然后是 name 字节:
var nameLen uint16 binary.Read(r, binary.BigEndian, &nameLen) name := make([]byte, nameLen) io.ReadFull(r, name) // 必须用 ReadFull,确保读满
解析失败时如何定位是哪一段出问题
二进制协议容错性极差:一个字节错,后面全乱。别在顶层 catch io.ErrUnexpectedEOF 就完事。应在每个关键字段读取后加校验:
- 检查 magic 是否匹配预期值
- 验证 length 字段是否过大(比如 > 1MB),防止内存爆炸
- 读完所有字段后,确认
r.Len()(如果是bytes.Reader)或用bytes.Equal校验 CRC/SHA
调试时临时加日志打印已读字节数和当前 offset,比盲猜快得多。用 hex.Dump 打印原始字节,对照协议文档逐字节核对。
协议越简单,越要警惕隐含约束:比如某个字段只允许 0–3 的枚举值,但解析时没做范围检查,后续逻辑就可能 panic。











