Go的encoding/binary包不自动识别字节序,必须显式指定binary.BigEndian或binary.LittleEndian;读写时需严格匹配协议字节序、字段顺序与类型大小,避免因长度不足、偏移错误、类型不匹配或平台差异导致数据解析失败。

Go 的 encoding/binary 包本身不自动识别字节序,读写大端/小端全靠你显式选对函数 —— 用错一个,数据就翻车。
怎么选 binary.Read 和 binary.Write 的字节序参数
这两个函数第三个参数必须传入一个 binary.ByteOrder 实例,只有两个合法值:binary.BigEndian 和 binary.LittleEndian。它不猜、不推断、不兼容旧协议 —— 你给啥,它就按啥解。
- 网络协议(如 IP、TCP 头)、Java/Python 默认的
struct.pack(">i")、标准二进制格式(PNG、ELF)通常用大端 → 用binary.BigEndian - x86/x64 CPU 原生内存布局、Windows API、C
ntohl()反向结果、大多数 Gounsafe场景默认是小端 → 用binary.LittleEndian - 如果和 C 程序通信,务必确认对方 struct 是怎么
#pragma pack和怎么序列化的;uint32在 C 里是小端,不代表你用binary.Read(buf, binary.LittleEndian, &x)就一定对 —— 还要看 buf 本身是不是已按小端排列
binary.Read 读 []byte 时常见错误:长度不够或偏移错位
传进去的 []byte 必须至少包含目标类型的字节数(比如读 uint32 至少要 4 字节),且起始位置要对齐。它不会跳过 padding,也不会帮你截断。
- 错误现象:
binary.Read返回io.ErrUnexpectedEOF—— 很可能 buf 长度只有 3 字节却想读uint32 - 错误现象:读出的数值明显异常(比如
0x00000001变成0x01000000)—— 很可能是字节序选反了,或者你从 buf[1:] 开始读,但没意识到前一个字节是 header - 安全做法:先检查
len(buf) >= binary.Size(&target),再用bytes.NewReader(buf)包一层,避免手动算偏移出错
用 binary.Write 写结构体时,字段顺序和对齐必须和二进制协议一致
binary.Write 不处理 struct tag(比如 `binary:"-"` 或 `json:"foo"`),它只按字段声明顺序、原生大小、原生对齐写入 —— 没有 magic,也没有 schema。
立即学习“go语言免费学习笔记(深入)”;
- 如果你的 struct 里有
int,它在 64 位系统上占 8 字节,但协议只要求 4 字节int32→ 必须显式声明为int32,否则写出来多 4 字节,下游直接解析失败 - struct 中有
byte+uint32,CPU 可能自动填充 3 字节对齐,但binary.Write不填 —— 它严格按字段类型大小拼接,所以byte后紧跟uint32的 4 字节,共 5 字节,无 padding - 想跳过某个字段?不能靠 tag,只能拆成多个
binary.Write调用,或手动生成子 slice
性能与跨平台陷阱:binary.Size 和 unsafe.Sizeof 不等价
binary.Size(x) 返回的是该值序列化后的字节数,它只看类型定义,不看运行时值;而 unsafe.Sizeof 返回的是内存中该变量的对齐后大小 —— 两者在 slice、string、指针类型上完全不同。
- 对
[]byte调用binary.Size返回 0(因为它只序列化 header,不是底层数组);想写整个 slice 数据,得先写 len,再写[]byte内容本身 - 对
string同理:返回 0;必须手动写len(s)+[]byte(s) - 跨平台代码里别假设
int是 4 字节 ——binary.Size(int(0))在 32 位和 64 位 Go 运行时返回不同结果(4 或 8),协议固定字段必须用int32/int64
最常被忽略的点:二进制协议文档里写的 “MSB first” 是指整个字段的最高有效字节排最前,不是指每个 nibble;而 Go 的 binary.BigEndian 正好对应这个定义 —— 但如果你误把一串 hex dump 当作小端解读,再用 LittleEndian 去读,结果连符号位都会错。










