C 网络编程中的数据对齐
关于数据对齐,我有点困惑。在x86上,我们通常认为对齐是理所当然的。然而,我在一个非常严格的系统上编程,如果我试图访问未对齐的数据,就会出错 我的问题是: 首先,我将向您展示我拥有的一些结构:C 网络编程中的数据对齐,c,networking,alignment,C,Networking,Alignment,关于数据对齐,我有点困惑。在x86上,我们通常认为对齐是理所当然的。然而,我在一个非常严格的系统上编程,如果我试图访问未对齐的数据,就会出错 我的问题是: 首先,我将向您展示我拥有的一些结构: struct sniff_ethernet { u_char ether_dhost[6]; /* Destination host address */ u_char ether_shost[6]; /* Source host address */ u_short ether_type;
struct sniff_ethernet {
u_char ether_dhost[6]; /* Destination host address */
u_char ether_shost[6]; /* Source host address */
u_short ether_type; /* IP? ARP? RARP? etc */
};
struct sniff_ip {
u_char ip_vhl; /* version << 4 | header length >> 2 */
u_char ip_tos; /* type of service */
u_short ip_len; /* total length */
u_short ip_id; /* identification */
u_short ip_off; /* fragment offset field */
u_char ip_ttl; /* time to live */
u_char ip_p; /* protocol */
u_short ip_sum; /* checksum */
struct in_addr ip_src,ip_dst; /* source and dest address */
};
让我们假设数据包是几百个字节。我通常要做的是将数据包转换成几个结构指针,以便直接访问数据
struct sniff_ethernet *seth = (struct sniff_ethernet *) packet;
struct sniff_ip *sip = (struct sniff_ip *) (packet + 14); // 14 is the size of an ethernet header
嗯。所以一切看起来都很好,对吗?在x86上,一切似乎都正常工作。在任何其他具有严格对齐的架构上,我在访问某些值时遇到问题,这通常会导致sigbus。例如:
sip->ip_len = 0x32AA;
或
导致错误。我猜是因为演员的记忆不一致。在执行此类强制转换时,处理此问题的最佳方法通常是什么?简单的方法是使用
memcpy
struct sniff_ip sip;
memcpy(&sip, packet + 14, sizeof(sip));
这假设您的两台机器使用相同的字节顺序,并且仔细考虑了结构填充
更难、更通用的处理方法是从单个字节构造值:
u_short val;
int offset = 14 + offsetof(sniff_ip, ip_len);
val = packet[offset] + (packet[offset+1] << 8); // assuming little endian packet
u_short val;
int offset=14+offsetof(嗅探ip,ip长度);
val=packet[offset]+(packet[offset+1]而不是使用+
和是,但这假设结构最初是使用网络顺序(大端)填充的。我从问题中得到的印象是,它们是使用x86(小端)顺序填充的。从问题中,“pcap将向我返回指向数据包的指针”表示代码正在接收数据包;以太网类型和IP头中的字段都是大端,而不是小端。x86与非x86的问题在于对齐要求,而不是字节顺序;x86则不是(默认情况下)需要对齐,但其他一些指令集体系结构(例如SPARC,它在默认情况下恰好是big-endian)确实需要对齐。在gcc中,您有_属性_((压缩))来告诉编译器在没有任何填充的情况下紧密对齐结构。__属性__((压缩))
在这种情况下没有区别,因为这些结构没有填充。
struct sniff_ip sip;
memcpy(&sip, packet + 14, sizeof(sip));
u_short val;
int offset = 14 + offsetof(sniff_ip, ip_len);
val = packet[offset] + (packet[offset+1] << 8); // assuming little endian packet