在套接字上发送一个结构,在C中使用正确的填充和字节序

我有几个结构定义为通过不同的操作系统(tcp网络)发送。 定义的结构是:

struct Struct1 { uint32_t num; char str[10]; char str2[10];} struct Struct2 { uint16_t num; char str[10];} typedef Struct1 a; typedef Struct2 b; 

数据存储在文本文件中。 数据格式如下:

  • 123
  • 馅饼
  • 脆皮

Struct1a存储为3个单独的参数。 但是,struct2是两个独立的参数,第二行和第三行都存储在char str []中。 问题是当我通过多个网络写入服务器时,数据未正确接收。 有许多空间可以分隔结构中的不同参数。 当我写入服务器时,如何确保正确的发送和填充? 如何正确存储数据(动态缓冲区或固定缓冲区)?

write的例子:write(fd,&a,sizeof(typedef struct a)); 它是否正确?

问题接收struct2的侧输出:

  • 123(,)
  • 0(,馅饼)
  • 0(地壳)

正确的输出

123(馅饼,地壳)

write(fd,&a, sizeof(a)); 是不正确的; 至少不可移植,因为C编译器可能在元素之间引入填充以确保正确对齐。 sizeof(typedef struct a)甚至没有意义。

您应该如何发送数据取决于协议的规格。 特别是,协议定义了发送字符串的各种方式。 分别发送struct成员通常是最安全的; 通过多次调用write或writev(2) 。 例如,发送

 struct { uint32_t a; uint16_t b; } foo; 

通过网络, foo.afoo.b已经有正确的字节顺序,你可以这样做:

 struct iovec v[2]; v[0].iov_base = &foo.a; v[0].iov_len = sizeof(uint32_t); v[1].iov_base = &foo.b; v[1].iov_len = sizeof(uint16_t); writev(fp, v, 2); 

通过网络发送结构非常棘手。 您可能遇到以下问题

  1. 字节endiannes问题与整数。
  2. 填充由您的编译器引入。
  3. 字符串解析(即检测字符串边界)。

如果性能不是您的目标,我建议为每个要发送和接收的结构创建编码器和解码器(ASN.1,XML或自定义)。 如果真的需要性能,你仍然可以使用结构和求解(1),通过修复字节序(即网络字节顺序)并确保你的整数存储在这些结构中,以及(2)通过修复编译器和使用编译指示或属性来强制执行“打包”结构。

例如,Gcc使用属性 (( packed )):

 struct mystruct { uint32_t a; uint16_t b; unsigned char text[24]; } __attribute__((__packed__)); 

(3)不容易解决。 在网络协议上使用空终止字符串并根据它们存在将使您的代码容易受到多次攻击。 如果需要涉及字符串,我会使用适当的编码方法,例如上面建议的方法。

简单的方法是为每个结构编写两个函数:一个用于从文本表示转换为结构,另一个用于将结构转换回文本。 然后,您只需通过网络发送文本,然后在接收方将其转换为您的结构。 这样的字节顺序无关紧要。

有转换函数可确保网络中二进制整数的可移植性。 使用htons,htonl,ntohs和ntohl将16位和32位整数从主机转换为网络字节顺序,反之亦然。