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

时间:2011-04-14 07:43:37

标签: c tcp padding endianness struct

我有几个结构被定义为通过不同的操作系统(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

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

写例:write(fd,& a,sizeof(typedef struct a));这是对的吗?

struct2的问题接收侧输出:

  • 123(,)
  • 0(,Pie)
  • 0(地壳)

正确输出

123(馅饼,地壳)

4 个答案:

答案 0 :(得分:10)

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

如何发送数据取决于协议的规格。特别是,协议定义了发送字符串的各种方式。分别发送struct成员通常最安全;通过多次写入或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 :(得分:3)

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

  1. 字节endiannes问题与整数。
  2. 编译器引入的填充。
  3. 字符串解析(即检测字符串边界)。
  4. 如果性能不是您的目标,我建议为每个要发送和接收的结构创建编码器和解码器(ASN.1,XML或自定义)。如果确实需要性能,你仍然可以使用结构并通过修复字节序(即网络字节)来解决(1) 确保你的整数存储在这些结构中,并且(2)通过修复编译器并使用pragma或属性来强制执行" packed"结构

    Gcc例如使用属性((打包)):

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

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

答案 2 :(得分:1)

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

答案 3 :(得分:1)

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