在 C 中通过具有正确填充和字节序的套接字发送结构

Posted

技术标签:

【中文标题】在 C 中通过具有正确填充和字节序的套接字发送结构【英文标题】:Send a struct over a socket with correct padding and endianness in C 【发布时间】:2011-08-05 07:52:45 【问题描述】:

我定义了几个结构以通过不同的操作系统(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 是两个单独的参数,第 2 行和第 3 行都存储到 char str[] 中。问题是当我通过多个网络写入服务器时,没有正确接收数据。有许多空格分隔结构中的不同参数。写入服务器时如何确保正确发送和填充?如何正确存储数据(动态缓冲区或固定缓冲区)?

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

struct2 的接收端输出问题:

123( , ) 0(,派) 0(地壳,)

正确的输出

123(馅饼,地壳)

【问题讨论】:

您应该发布您用于发送和接收的代码的 sn-ps,我们无法真正猜出您做错了什么。 看看RFC 4506 XDR: External Data Representation Standard,它描述了如何对数据进行编码以便在机器之间传输。 【参考方案1】:

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);

【讨论】:

htons 和 htonl 是您将 16 位和 32 位整数转换为网络字节序的朋友。 ntohs 和 ntohl 将它们转换回来。 @larsmans 这里是一个代码 sn-p 读取数据到 struct ` ` write(sockfd,&v[0],htons(sizeof(v[0]))); ` ' struct ver1 uint16_t id;字符名称[20]; v[20]; // #pragma pack(0) struct ver2 uint32_t pid;字符 fname[20];字符 lname[20]; r[20]; '【参考方案2】:

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

    整数的字节字节序问题。 您的编译器引入了填充。 字符串解析(即检测字符串边界)。

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

例如,Gcc 使用 attribute((packed)) :

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

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

【讨论】:

如何将其发送(写入)到服务器?写(fd,______,sizeof(mystruct))? 确实如此。这种技术在低级驱动程序中相当常用。【参考方案3】:

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

【讨论】:

【参考方案4】:

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

【讨论】:

以上是关于在 C 中通过具有正确填充和字节序的套接字发送结构的主要内容,如果未能解决你的问题,请参考以下文章

在 Python 中通过套接字发送大量数据的正确方法是啥?

在 C++ 中通过 'recv' 和 'MSG_PEEK' 获取套接字中可用的字节数

在 pytorch 中通过具有线性输出层的 RNN 发送的填充批次的掩码和计算损失

在 C 中通过套接字发送 mp3 文件

使用套接字在eclipse android程序中通过IP地址发送消息

C++socket编程,数据转为网络字节序的问题htons