在Python中计算结构体的CRC

5

我有以下结构体,来自C语言中的NRPE守护进程代码:

typedef struct packet_struct {
  int16_t packet_version;
  int16_t packet_type;
  uint32_t crc32_value;
  int16_t result_code;
  char buffer[1024];
} packet;

我想从Python向C守护进程发送这种数据格式。当crc32_value0时,计算CRC并将其放入结构体中。我用以下Python代码实现:

cmd = '_NRPE_CHECK'
pkt = struct.pack('hhIh1024s', 2, 1, 0, 0, cmd)
# pkt has length of 1034, as it should
checksum = zlib.crc32(pkt) & 0xFFFFFFFF
pkt = struct.pack('hhIh1024s', 2, 1, checksum, 0, cmd)
socket.send(....)

守护进程正在接收这些值:version=2 type=1 crc=FE4BBC49 result=0,但它计算的是crc=3731C3FD
实际的C代码用于计算CRC是: https://github.com/KristianLyng/nrpe/blob/master/src/utils.c 并通过以下方式调用: calculate_crc32((char *)packet, sizeof(packet)); 当我将这两个函数转移到Python时,我得到了与zlib.crc32返回的相同结果。
我的struct.pack调用是否正确?为什么我的CRC计算结果与服务器不同?

1
你确定两个平台都使用相同的对齐方式吗? - Paulo Scardine
可能是字节顺序问题。服务器可能使用网络(大端)顺序,但您的脚本使用本机顺序,因为它在格式字符串开头没有指定任何内容并覆盖了默认值 - 请参见在线文档中的部分。 - martineau
可能性:(1)字节序差异(2)字节交换差异(3)填充[对齐]差异。 - wberry
1
@wberry:1)和2)基本上不是同一件事吗? - martineau
实际上不是这样的。在#1中,你可以将同一个字节读取为0xE2或0x47。在#2中,字节可以被存储为"UNIX"或"NUXI"。 - wberry
显示剩余2条评论
1个回答

2

来自Python结构文档

为了处理平台无关的数据格式或省略隐含的填充字节,使用标准大小和对齐方式而不是本地大小和对齐方式:有关详细信息,请参见字节顺序、大小和对齐方式

使用'!'作为第一个格式字符,使打包的结构与平台无关。它强制使用大端序、标准类型大小和没有填充字节。然后CRC应该是一致的。


在开头使用'>'可以得到正确的字节序,但出于某种原因,我还必须在末尾添加一个字节。该C结构的大小为1036,而不是1034;不知道为什么... - verideskstar
1
@sadris:很可能是由于C/C++结构填充,将C-struct的大小填充到最接近的32位(4字节)本机字长。一些编译器有一个选项可以打开和关闭这个功能。 - martineau
只需比较1034个字节,而不是试图将其填充到1036个字节。C结构的最后两个填充字节可能未初始化,因此您添加的填充将不匹配。除非您从C结构中复制它,否则这样做就很愚蠢。 - Mark Adler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接