如何在Python中解析IPv4报文头部并填充至自定义类实例
问题描述
我有一段如下所示的C语言代码:
struct iphdr{ #if __BYTE_ORDER == __LITTLE_ENDIAN unsigned int ihl: 4; unsigned int version: 4; #elif __BYTE_ORDER == __BIG_ENDIAN unsigned int version: 4; unsigned int ihl: 4; #else #error "Please fix <bits/endian.h>" #endif uint8_t tos; uint16_t tot_len; uint16_t id; uint16_t frag_off; uint8_t ttl; uint8_t protocol; uint16_t check; uint32_t saddr; uint32_t daddr; }; ... ... ... unsigned char * ip_header_bytes = "\x45\x00\x00\xf8\xb8\xa7\x00\x40\x06\x81\x80\xac\x10\x44\x03\x22\x78\xed\x4c"; struct iphdr * ipdata = (struct iphdr *)ip_header_bytes;
通过这段代码,我可以直接将字节数据强转为结构体指针,访问ipdata中的各个成员。现在我在Python中定义了一个与iphdr结构类似的类,且拥有与ip_header_bytes相同的字节数据,请问如何将该字节数据填充到该类的新实例中?
解决方案
Python没有原生的结构体位域语法,需要借助struct模块解析字节数据,手动处理位域拆分后,再映射到自定义类的实例上。以下是两种常用实现方式:
方法1:手动解析+实例赋值
先定义对应类,再通过struct.unpack解析字节,处理位域后赋值给类实例:
import struct class IpHdr: def __init__(self, version, ihl, tos, tot_len, id, frag_off, ttl, protocol, check, saddr, daddr): self.version = version self.ihl = ihl self.tos = tos self.tot_len = tot_len self.id = id self.frag_off = frag_off self.ttl = ttl self.protocol = protocol self.check = check self.saddr = saddr self.daddr = daddr # 目标字节数据(注意转为bytes类型) ip_header_bytes = b"\x45\x00\x00\xf8\xb8\xa7\x00\x40\x06\x81\x80\xac\x10\x44\x03\x22\x78\xed\x4c" # 按IP头部的大端字节序解包:第一个字节包含version和ihl,后续依次匹配C结构体类型 unpacked_data = struct.unpack(">B B H H H B B H I I", ip_header_bytes) first_byte = unpacked_data[0] # 拆分位域:大端序下第一个字节高4位是version,低4位是ihl version = (first_byte >> 4) & 0x0F ihl = first_byte & 0x0F # 创建类实例并传入解析后的数据 ipdata = IpHdr(version, ihl, *unpacked_data[1:]) # 验证示例 print(ipdata.version) # 输出4 print(ipdata.ihl) # 输出5
方法2:类内部封装解析逻辑
把字节解析逻辑封装为类方法,更符合面向对象的使用习惯:
import struct class IpHdr: def __init__(self, version, ihl, tos, tot_len, id, frag_off, ttl, protocol, check, saddr, daddr): self.version = version self.ihl = ihl self.tos = tos self.tot_len = tot_len self.id = id self.frag_off = frag_off self.ttl = ttl self.protocol = protocol self.check = check self.saddr = saddr self.daddr = daddr @classmethod def from_bytes(cls, byte_data): # 按大端序解包字节数据 unpacked = struct.unpack(">B B H H H B B H I I", byte_data) first_byte = unpacked[0] # 拆分位域 version = (first_byte >> 4) & 0x0F ihl = first_byte & 0x0F # 创建并返回类实例 return cls(version, ihl, *unpacked[1:]) # 使用示例 ip_header_bytes = b"\x45\x00\x00\xf8\xb8\xa7\x00\x40\x06\x81\x80\xac\x10\x44\x03\x22\x78\xed\x4c" ipdata = IpHdr.from_bytes(ip_header_bytes) # 访问成员示例 print(ipdata.tot_len) # 输出248(对应0x00f8) print(ipdata.saddr) # 输出3232243715(对应0xac104403)
关键注意事项
- 字节序匹配:IP头部采用网络字节序(大端),所以
struct.unpack使用>前缀;如果处理的是小端环境的非网络数据,要替换为<前缀。 - 位域手动处理:Python没有原生位域支持,必须通过位移和掩码操作拆分第一个字节的高低4位,对应C结构体中的
version和ihl。 - 类型严格对应:
struct格式符要与C结构体类型一一匹配:- C的
uint8_t对应B uint16_t对应Huint32_t对应I
- C的
内容的提问来源于stack exchange,提问作者raging-loon
相关产品推荐
相关产品推荐

