1

我正在尝试为 CAPWAP 协议创建客户端 C 代码。我尝试使用位字段结构实现 CAPWAP 标头。但是在使用 sendto() 通过套接字发送这个结构之后,当我使用wireshark 嗅探数据包时,我发现在它们之间添加了一些额外的位。我不知道这是从哪里来的。请求帮助。提前致谢。

线鲨

我尝试评论结构的最后几个成员以使其 4 字节对齐。问题仍然存在。

这是原始标题

struct cw_header
{
unsigned preamble : 8;
unsigned hlen : 5;
unsigned rid : 5;
unsigned wbid : 5;
unsigned t : 1;
unsigned f : 1;
unsigned l : 1;
unsigned w : 1;
unsigned m : 1;
unsigned k : 1;
unsigned flags : 3;
unsigned fragment_id : 16;
unsigned fragment_offset : 13;
unsigned reserved : 3;
uint32_t mac_length : 8;
uint32_t mac_addr[6];
uint32_t padding : 8;
};

我试着评论这些

//uint32_t mac_length : 8;
//uint32_t mac_addr[6];
//uint32_t padding : 8;

这是填充结构的地方

struct cw_header create_cw_header()
{
struct cw_header cw_header;
cw_header.preamble = 0;
cw_header.hlen = 1;
cw_header.rid = 1;
cw_header.wbid = 1; 
cw_header.t = 0;
cw_header.f = 0;
cw_header.l = 1;
cw_header.w = 0;
cw_header.m = 1;
cw_header.k = 0;
cw_header.flags = 0;    
cw_header.fragment_id = 0;  ;
cw_header.fragment_offset = 0;  
cw_header.reserved = 0; 
cw_header.mac_length = 6;
get_mac_address(cw_header.mac_addr);
cw_header.padding = 0;
return cw_header;
};

这是前 32 位的wireshark 的输出

0000 0000 0010 0001 0000 0100 0000 1010 

预期结果:位应按结构中提到的顺序错误:在结构成员之间添加额外位

4

2 回答 2

5

“位应该按照结构中提到的顺序”

什么顺序?C 语言没有指定位的顺序。您无法轻松知道preamble是 MSB 还是 LSB。

“在结构成员之间添加了额外的位”

是的,编译器可以自由地在位域的成员之间放置填充位或填充字节。位域被标准划分为抽象的、“不可见的”单元,称为“存储单元”,它们具有给定的大小——通常与 CPU 对齐的大小相同,但不一定如此。在不同的此类存储单元之间可能存在或不存在填充。编译器还可以自由地将不适合一个存储单元的位放置在下一个存储单元中。

如果不阅读编译器的位域实现,就无法知道特定编译器会做什么。C 标准几乎不支持它们。

在此之上添加字节序,你就会有一个适当的混乱 - 在你的情况下,CPU字节序和网络字节序都很重要。

所有这些问题的最佳解决方案是完全删除位域。相反,您应该使用定义更明确、确定性和可移植性更高的位运算符:

uint32_t cw_preable = something << preamble_offset;
...
uint32_t cw_header = 0;
cw_header |= cw_preamble;
...
于 2019-08-06T06:48:45.160 回答
3

位域的布局完全依赖于实现。然而,一个单元不能跨越存储单元边界是很常见的,所以在这种情况下,它取决于大小unsigned。它也是依赖于实现的,它是如何uint32_t定义的,它如何与其他所有东西进行比较,以及是否uint32_t mac_addr[6];可以覆盖uint32_t : 8.

总而言之,它不会很漂亮。最好的做法是unsigned char/uint8_t在结构中使用命名字段和数组,并将较小的成员打包到这些中。

于 2019-08-06T06:47:08.363 回答