C 在发送数据包时如何避免填充

C 在发送数据包时如何避免填充,c,C,我想用write systemcall发送一个数据包。我创建了这个数据包,它的大小是46字节。当我用wireshark发送程序中的数据包时,长度是60字节,14字节是填充。 我如何在没有填充的情况下发送数据包 我不能使用套接字命令 struct x { bool is_active; bool is_owner; int fd1; int fd2; char pkt_hdr[sizeof(struct ether_header) + sizeof(struct ip)]; struc

我想用write systemcall发送一个数据包。我创建了这个数据包,它的大小是46字节。当我用wireshark发送程序中的数据包时,长度是60字节,14字节是填充。 我如何在没有填充的情况下发送数据包

我不能使用套接字命令

struct x {
 bool is_active;
 bool is_owner;
 int fd1;
 int fd2;
 char pkt_hdr[sizeof(struct ether_header) + sizeof(struct ip)];
 struct interface *ifp;
 int fd_bufflen;
 struct ipaddr src;
 uint8_t ibuf[IP_MAXPACKET];
 int family;
 struct vrrp_vrouter *vr;
 struct list *addrs;
 bool ad;
 bool ga;
 bool nd;
 uint8_t priority;
 uint16_t ma_val;
 uint16_t sk_tm;
 uint16_t ma_val;
 struct ethaddr vmac;
  struct {
    int state;
  } fsm;

  struct {
    uint32_t ad_cnt;
    uint32_t ar_cnt;
    uint32_t g_cnt;
    uint32_t un_cnt;
    uint32_t t_cnt;
  } stats;

  struct thread *t_ti;
  struct thread *t_adti;
  struct thread *t_read;
  struct thread *t_write;
};
.
.
.
size_t buf_len = pktsz + sizeof r->pkt_hdr;
. 
.
.

/* Ethernet Header */
struct ether_header *ether_hdr = (struct ether_header *)buf;
.
.
.

/* IP Header */

struct ip *ip_hdr = (struct ip *) (ether_hdr + 1);
ip_hdr->ip_len = htons(buf_len - sizeof(struct ether_header));
ip_hdr->ip_src = r->src.ipaddr_v4;
ip_hdr->ip_sum = in_cksum(ip_hdr, sizeof(struct ip));

/* Payload */
memcpy((void *)(buf + sizeof r->pkt_hdr), (const void *)pkt, pktsz);

ssize_t sent = write(fd, buf, buf_len);
free(buf);
布夫伦:46

pktsz:12

尺寸r->pkt\U hdr:34


长度为46。

不幸的是,在存储协议时,struct通常非常糟糕,因为它们需要对齐和填充。这意味着您不能在整个结构上使用
memcpy
etc。相反,
struct
必须在通过数据通信协议发送之前序列化,以放弃填充

简化示例:

typedef struct
{
  int i;
  char c;
} foo_t;

void foo_serialize (uint8_t dst[5], const foo_t* src)
{
  memcpy(dst, &src->i, 4);
  dst[4] = src->c;
}
接收器端需要一个类似的反序列化例程

另一种选择是用非标准的
#pragma pack(1)
和类似的东西“打包”结构。这会删除填充,但填充是有原因的。如果打包结构,程序可能最终读取时未对齐,这可能导致代码速度变慢或某些系统硬件出现异常


但同时你也需要转换到网络端。Big-endian通常用于大多数数据协议中。重写上面的示例,将其从little转换为big endian:

void foo_serialize (uint8_t dst[5], const foo_t* src)
{
  dst[0] = ((uint32_t)src->i >> 24) & 0xFFu;
  dst[1] = ((uint32_t)src->i >> 16) & 0xFFu;
  dst[2] = ((uint32_t)src->i >>  8) & 0xFFu;
  dst[3] = ((uint32_t)src->i >>  0) & 0xFFu;
  dst[4] = src->c;
}

还有POSIX
htonl
功能(主机端到网络端,长格式),但由于该功能不能在POSIX PC计算机之外移植,因此它有点违背了自己的目的

不幸的是,
struct
在存储协议时通常非常糟糕,因为它们需要对齐和填充。这意味着您不能在整个结构上使用
memcpy
etc。相反,
struct
必须在通过数据通信协议发送之前序列化,以放弃填充

简化示例:

typedef struct
{
  int i;
  char c;
} foo_t;

void foo_serialize (uint8_t dst[5], const foo_t* src)
{
  memcpy(dst, &src->i, 4);
  dst[4] = src->c;
}
接收器端需要一个类似的反序列化例程

另一种选择是用非标准的
#pragma pack(1)
和类似的东西“打包”结构。这会删除填充,但填充是有原因的。如果打包结构,程序可能最终读取时未对齐,这可能导致代码速度变慢或某些系统硬件出现异常


但同时你也需要转换到网络端。Big-endian通常用于大多数数据协议中。重写上面的示例,将其从little转换为big endian:

void foo_serialize (uint8_t dst[5], const foo_t* src)
{
  dst[0] = ((uint32_t)src->i >> 24) & 0xFFu;
  dst[1] = ((uint32_t)src->i >> 16) & 0xFFu;
  dst[2] = ((uint32_t)src->i >>  8) & 0xFFu;
  dst[3] = ((uint32_t)src->i >>  0) & 0xFFu;
  dst[4] = src->c;
}

还有POSIX
htonl
功能(主机端到网络端,长格式),但由于该功能不能在POSIX PC计算机之外移植,因此它有点违背了自己的目的

打包防止编译器在GCC下使用\uuuuu属性((\uuuu packed\uuuu))进行填充-这必须明确请求

有关更多说明,请浏览以下链接:

打包防止编译器在GCC下使用\uuuuu属性((\uuuu packed\uuuu))进行填充-这必须明确请求

有关更多说明,请浏览以下链接:

尝试重新安排结构成员以减少填充字节。建议您也提交代码,否则,这将只是一个猜测游戏@th33lf我编辑了帖子并添加了一部分代码。@Sanjeev:一些硬件在未对齐访问时抛出异常。现在的问题不是如何填充/打包结构,它是否需要序列化?@truthseek甚至x86都可能在由打包结构导致的未对齐访问上爆炸:尝试重新排列结构成员以减少填充字节。建议也放置代码,否则,这将只是一个猜测游戏@th33lf我编辑了帖子并添加了一部分代码。@Sanjeev:一些硬件在未对齐访问时抛出异常。现在的问题不是如何填充/打包结构,它是否需要序列化?@truthseek甚至x86都可能在打包结构导致的未对齐访问中崩溃:。而且,在实现一个与wlan设备驱动程序对话并从驱动程序获取一些信息的应用程序时,我们也遇到了类似的问题。所以当时我们使用属性uu((_packed))解决了这个问题。但是在使用属性_uu((_packed))之后,我们必须非常小心结构大小@andrewhenlean通过这样做,您已经编写了代码,其中在代码的整个生命周期中接触代码的每个开发人员都必须意识到,永远不要获取该打包结构的任何成员的地址。您是否在代码中记录了这一点?“你是否在培训新雇佣的开发人员,让他们必须意识到这一点?”安德烈·惠恩勒显然我已经记录了这一点。我们这样做也有一些好处。而且我也不想和你争论。只是想让你知道我们可以这样做。谢谢而且,在实现一个与wlan设备驱动程序对话并从驱动程序获取一些信息的应用程序时,我们也遇到了类似的问题。所以当时我们使用属性uu((_packed))解决了这个问题。但是在使用属性_uu((_packed))之后,我们必须非常小心结构大小@andrewhenlean通过这样做,您已经编写了代码,其中在代码的整个生命周期中接触代码的每个开发人员都必须意识到,永远不要获取该打包结构的任何成员的地址。您是否在代码中记录了这一点?“你是否在培训新雇佣的开发人员,让他们必须意识到这一点?”安德烈·惠恩勒显然我已经记录了这一点。我们这样做也有一些好处。而且我也不想和你争论。只是想让你知道我们可以这样做。谢谢