从字节数组解包整数

时间:2019-01-08 19:29:08

标签: c network-programming endianness

如果这很明显,请原谅我,但是我正在使用字节数组通过网络发送数据,我需要将一个整数填充到其中,然后从另一端取回它。

示例中的类型定义:

uint8_t *dest;
uint8_t *ciphertext;
size_t  cbytes; // length of ciphertext
uint8_t iv[16];
uint8_t tag[16];

作者的相关部分:

size_t bytes = 0;
memcpy(&dest[bytes], iv, sizeof(iv));
bytes = sizeof(iv);
memcpy(&dest[bytes], (void*)htonl(cbytes), sizeof(uint32_t));
bytes += sizeof(uint32_t);
memcpy(&dest[bytes], ciphertext, cbytes);
bytes += cbytes;
memcpy(&dest[bytes], tag, sizeof(tag));
bytes += sizeof(tag);

这是将cbytes(作为整数填充到字节数组中)的正确方法吗?如果没有,有什么更好的方法呢?

现在,使用此字节数组,如何将cbytes读回整数(或size_t)?其余的只能复制回去,但不确定如何处理整数。

1 个答案:

答案 0 :(得分:1)

您正在询问这段代码:

memcpy(&dest[bytes], (void*)htonl(cbytes), sizeof(uint32_t));

不,这根本不正确。您正在将htonl的返回值转换为指针。但是,它不是有效的指针。您必须具有一个uint32_t类型的对象用于发送:

uint32_t cbytes32 = htonl(cbytes);
memcpy(&dest[bytes], &cbytes32, sizeof(uint32_t));

在现代C语言中,也可以使用复合文字在一行上执行此操作,以创建一个uint32_t个内联数组:

memcpy(&dest[bytes], (uint32_t[]){ htonl(cbytes) }, sizeof(uint32_t));

但是语法确实看起来没有任何改善。

要读入,您需要先将其读为uint32_t类型的对象,然后再将其读为ntohl,然后将返回值存储到size_t中:

uint32_t size32;
size_t size;
memcpy(&size32, src, sizeof size32)
size = ntohl(size32);

然后,我要特别小心,因为您正在其他地方使用可能的64位size_t,但在此处将其截断为32位。可能还可以,但是这需要记录在案。 64位对于每个人都足够了,但是不幸的是没有htonll函数。


最后,您可以编写&dest[bytes]来代替dest + bytes来减少按键操作。而且,甚至可以花更少的钱创建另一个指针:

uint8_t *cur = dest;
memcpy(cur, iv, sizeof iv);
cur += sizeof iv;
uint32_t cbytes32 = htonl(cbytes);
memcpy(cur, &cbytes32, sizeof cbytes32);
cur += sizeof cbytes32;
memcpy(cur, ciphertext, cbytes32);
cur += cbytes32;
memcpy(cur, tag, sizeof tag);
cur += sizeof tag;
size_t nbytes = cur - dest;

请注意,如果您使用的是流套接字(TCP),通常无需将其复制到中间缓冲区中-只需使用单独的{{1} }调用-或至少不要在size调用之后费心将 long 数组复制到同一缓冲区中。