Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/149.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 通过网络发送结构_C++_Struct_Network Programming_Sizeof - Fatal编程技术网

C++ 通过网络发送结构

C++ 通过网络发送结构,c++,struct,network-programming,sizeof,C++,Struct,Network Programming,Sizeof,在我们公司,通过网络发送C/C++结构是非常常见的。任何结构都有一个或多个uint32\t字段: typedef struct { uint32_t data1; uint32_t data2; } Data; 字段(data1,data2,…)封装了我们需要发送的真实数据。我们的数据大部分是单个或几个位长的,因此为了节省空间/带宽,它在这些字段中的特定位位置对齐 为了访问真实数据,我们编写(在结构外部!)带有位移位和位掩蔽的“getter”和“setters”宏: #defi

在我们公司,通过网络发送C/C++结构是非常常见的。任何结构都有一个或多个uint32\t字段:

typedef struct {
    uint32_t data1;
    uint32_t data2;
} Data;
字段(data1,data2,…)封装了我们需要发送的真实数据。我们的数据大部分是单个或几个位长的,因此为了节省空间/带宽,它在这些字段中的特定位位置对齐

为了访问真实数据,我们编写(在结构外部!)带有位移位和位掩蔽的“getter”和“setters”宏:

#define READY_MASK 0x01      // indicates that READY is a single bit value
#define READY_OFFSET 3       // indicates position of READY inside 32-bit field
#define IS_READY(x) { ... }  // returns READY value from x field
#define SET_READY(x,r) { ... }  // sets READY value to x field
现在,我想通过将getter和setter直接添加到结构中来修改、简化并使此过程更加安全,例如:

typedef struct {
    uint32_t data1;

    #define READY_MASK 0x01
    #define READY_OFFSET 3
    inline void set_ready(uint32_t r) { /*...*/ }
    inline uint32_t is_ready() { /*...*/ }
    // lots of other getters and setters
} Data1;
只要我的实验是正确的,我注意到这种修改不会影响结构的大小
sizeof(Data)==sizeof(Data1)
,我可以通过网络发送此结构,并在另一端接收和解码


我的问题是:这项修改是否有任何错误?是否有任何风险或我应该注意的事项?

原则上,如果不添加虚拟函数,将不会有vptr,并且结构大小应与没有成员函数时相同。但它不再是一个豆荚结构,所以你可能很容易进入UB的暮色地带,未定义的行为。既然C++还没有标准化的ABI,那么最好采用C型POD结构,例如通过网络进行序列化。对于精简的、可移植的API,对于C++模块/子系统,坚持C abi。

同样适用。

此外,我建议你远离BiField,如果你考虑那些(如一些评论家建议的),手动掩码通常更容易分析和便携。例如,位字段可能会给您带来endianness问题,这至少部分是因为结构的第一个成员“始终”在结构开始时位于较低的内存地址,而不管endianness如何。位字段的工作方式也可能取决于传输介质,例如,BSD网络流套接字是字节流,但许多硬件接口复制整个32位字大小的块,而不是字节

您所做的修改没有任何危害,但也没有任何好处,因为结构成员无论如何都是公共的。如果您添加的是内联函数,它们不占用对象中的空间,而是用实际的函数代码替换任何函数调用,则说明Data和Data1大小相同的原因

现在,如果通过网络发送结构和二进制数据,则必须考虑以下两个规则:

  • 按照惯例,使用网络端号发送整数值,网络端号是大端号,而不是x86的小端号。如果您确定所有发送和接收数据的计算机都是x86,则这不是问题。但是,如果任何机器具有不同的Endian,例如Big-Endian或No-Endian(或Middle-Endian),您将遇到问题。例如,您可以使用ARM处理器、SPARC等。在C中,您可以使用以下宏:

    ntohs,htons,ntohl,htonl

  • <>你也必须考虑不同的内存对齐方式。结构的内存对齐取决于体系结构、编译器和编译模式。编译器可以添加填充(它们不能在C中对成员重新排序,但可以读取以下内容:)。此外,像long这样的类型在32位和64位体系结构中具有不同的大小。即使非常确定只有两个无符号int成员不会出现问题,也不应该将内存从结构复制到将在消息中发送的数据缓冲区,也不应该将内存从消息中的原始数据复制到结构。基本上你不应该这样做:

    字符缓冲区[BUF_大小]

    数据1我的数据

    memcpy(缓冲区、myData、sizeof(数据))

您应该逐个将成员移动到缓冲区。从消息中接收的原始数据填充结构时也是如此


我还建议不要使用位字段,请阅读。但非常清楚的是,在代码片段中没有使用位字段。使用位字段与使用整数中的位来表示某些特定状态不同。在这种情况下,你做的很好,应该只考虑具有不同顺序的体系结构中的整数表示(如果这是可能的情况)。<>代码> DATA1结构不是C结构,C结构不能有成员函数。你确定你不是在C++编程吗?假设它是C++(因为它只是在C中编译不出来),那么如何增加函数到结构中的安全性呢?当然,JoaHixIbRig,DATA1是C++,如果有人决定声明一个函数是虚拟的,那又会怎样呢?根据我的经验,依赖结构布局迟早会出现断裂。最好只在内存中使用结构,并创建适当的序列化函数将其转换为可传输的字节数据。我不会称之为“不安全”,但可能存在编译器特定的不兼容。就像所有结构一样。所有结构可能在编译器或平台之间的不同位置都有填充。这与网络处理或“安全性”无关,它是编译器和语言规范中固有的。将结构(带或不带位字段)保存到二进制文件,然后在另一个程序中读取它们,也会遇到同样的问题。如果您想独立于平台,则需要修改结构。