在 Python 开发中,利用 struct 模块按照自定义协议打包与解包 TCP 报文是一项极为常见的需求,例如快速构造和解析包含魔数、长度、类型以及变长数据体的二进制载荷。该协议格式相对简洁:4 字节魔数(大端序)、2 字节长度(大端序)、1 字节类型,其后紧随由长度字段所指定的数据体。其中,边界检查必须严格到位,否则接收方在遭遇损坏的字节流时,轻则解析出错,重则可能导致内存越界。

明确报文结构设计与字段约束
首先明确协议规范:魔数固定为 0x12345678(大端序),长度字段是紧随其后的 2 字节无符号整数(采用网络字节序),类型占 1 字节,数据体长度由长度字段精确指定——注意该长度不包含头部本身。此处的对齐必须严格无误,否则后续的封包与解包操作必然出现错位。
使用 Python struct 模块打包时,格式字符串需定义为 !IHB:其中 ! 代表大端 + 网络字节序,I 为 4 字节魔数,H 为 2 字节长度,B 为 1 字节类型。若遗漏叹号或误用小写 i / h,则会被本地字节序污染,导致跨平台收发时直接解析失败——这一点在调试过程中极易踩坑。
编写封包函数(pack_message)
创建函数 pack_message(msg_type: int, payload: bytes) -> bytes:
第一步:计算载荷长度,即 len(payload);第二步:通过 struct.pack("!IHB", 0x12345678, len(payload), msg_type) 打包头部;第三步:直接拼接头部与 payload 并返回完整的二进制字节串。注意 msg_type 须在 0–255 范围内,超限会触发 struct.error;payload 为空字节串(b"")是合法情形,此时长度字段为 0,不会产生歧义。
编写解包函数(unpack_message)
函数签名:unpack_message(data: bytes) -> tuple[int, bytes] | None,返回类型和数据体,失败则返回 None。先检查输入长度是否 ≥ 7 字节(4+2+1 最小头部长度),不足则直接返回 None;接着使用 struct.unpack_from("!IHB", data) 解出魔数、长度和类型;验证魔数是否等于 0x12345678,不等即丢弃;随后检查剩余长度能否容纳声明数据体——即 len(data) >= 7 + declared_length,否则存在截断风险;最后切片取出 payload 并返回 (msg_type, payload)。此处的边界检查是确保安全性的关键步骤,缺一不可。
生成附带测试用例的完整可执行脚本
在 VS Code 中新建 tcp_packet.py,粘贴以下内容即可直接运行:
① 导入 struct;② 定义 pack_message 和 unpack_message 函数;③ 编写三组测试:正常封包→解包→比对;超长 payload 封包→解包→验证长度;错误魔数数据→解包→返回 None;④ 每组测试后打印 print("PASS") 或 print("FAIL")。保存后在终端执行 python tcp_packet.py,若看到三行 PASS 则表明封包解包逻辑完全可靠——你可以将该脚本作为项目中的基础工具函数使用。
