我想使用htonl()和htos()将消息从主机字节顺序转换为网络顺序。在这条消息中,有一些复杂的定义数据类型slike结构,枚举,联合和联合。
typedef struct {
unsigned short un1_s;
unsigned char un1_c;
union {
unsigned short un1_u_s;
unsigned long un1_u_l;
}u;
}UN1;
typedef struct {
unsigned short un2_s1;
unsigned short un2_s2;
} UN2;
typedef enum {
ONE,
TWO,
TRHEE,
FOUR
} ENUM_ID;
typedef struct {
unsigned short s_sid;
unsigned int i_sid;
unsigned char u_char;
ENUM_ID i_enum;
union {
UN1 un1;
UN2 un2;
} u;
} MSG;
void msgTranslate (MSG* in_msg, MSG* out_msg){
/* ignore the code validating pointer ... */
*out_msg = *in_msg;
#ifdef LITLE_ENDIAN
/* translating messeage */
out_msg->s_sid = htons( in_msg->s_sid ); /* short */
out_msg->i_sid = htonl( in_msg->i_sid ); /* int */
/* Can I simply leave out_msg->u_char not to translate,
* because it is a single byte? */
out_msg->i_enum = htonl(in_msg->i_enum);
/* Can I simply translate a enum this way,? */
/* For an union whose 1st member is largest one in size than
* others, can I just translate the 1st one,
* leaving the others not to convert? */
out_msg->u.un1.un1_s = htons(in_msg->u.un1.un1_s);
/* for out_msg->u_char, can I simply leave it
* not to be converted, because it is a single byte? */
/* for an union whose 2nd member is largest one,
* can I just convert the 2nd one, leaving others
* not to be converted? */
out_msg->u.un1.u.un1_u_s = htos(in_msg->u.un1.u.un1_u_s ); /* short */
/* As above question, the following line can be removed?
* just because the u.un1.u.un2_u_i is smaller
* than u.un1.u.un1 in size ? */
out_msg->u.un1.u.un2_u_i = htol(in_msg->u.un1.u.un2_u_l ); /* long */
/* Since un1 is largest than un2, the coding translation un2 can be ignored? */
...
#endif
return;
}
答案 0 :(得分:3)
您需要适当地映射每个多字节类型。
对于联合,您需要确定哪个是联合的“活动”元素,并根据常规规则进行映射。您可能还需要提供一个“鉴别器”,告诉接收代码发送了哪种可能性。
对于枚举,您可以决定将所有此类值视为long
并相应地进行编码和解码。或者,您可以单独处理每个枚举,根据其大小处理每种类型(理论上,不同的枚举可以有不同的大小)。
这取决于你接下来要做什么。如果要打包数据以通过网络传输,则接收和发送操作会有所不同。如果您所做的只是在内存中的结构中翻转位,那么您可能会发现在大多数系统上,将htonl()
函数应用于htonl()
的结果的结果是您的第一个数字想到。如果您计划对映射(翻转)结构中的所有字节进行二进制复制,那么您可能做得不对。
请注意,您的数据结构在大多数似乎合理的系统上都有各种填充孔。在结构UN1中,如果它是32位系统,几乎可以肯定在un1_c
和后面的u
之间有一个填充字节;如果它是64位系统,你可能有5个字节的填充。同样,在MSG
结构中,s_sid
之后可能有2个填充字节,u_char
之后可能有3个填充字节。根据{{1}}的大小(以及您是在32位还是64位计算机上),enum
之后可能有1-7个字节的填充。
请注意,由于您没有数据类型的独立于平台的大小,因此无法在32位和64位Unix系统之间可靠地进行互操作。如果这些系统都是Windows,那么在32位和64位Windows上都可以使用i_enum
。但是,基本上所有的64位Unix变种sizeof(long) == 4
。因此,如果跨平台工作是一个问题,您必须担心这些大小以及填充。调查sizeof(long) == 8
标题中的类型,例如<inttypes.h>
和uint16_t
。
您应该在所有主机上执行相同的打包,小心地将各种值的字节复制到字符缓冲区中的适当位置,然后通过线路发送并通过反向编码解压缩。
同时查看Google的Protocol Buffers是否会合理地为您完成这项工作;它可能会为你节省大量的痛苦和悲伤。
答案 1 :(得分:2)
enum
可能是任意大小,具体取决于您的平台(请参阅What is the size of an enum in C?)。工会很难处理。比如说,我将值0x1234存储在big-endian中short
的{{1}}中。然后,union包含字节union {short; long;}
,因为short占用了union的低两个字节。如果您以12 34 00 00
结束翻转,则会得到long
,这会产生00 00 34 12
0x0000。如果您以short
结尾,则获得short
。我不确定你认为哪一个是正确的,但很明显你有问题。
在这样的联合中有两个34 12 00 00
更典型,其中一个short
是低半字,另一个是高半字。哪一个取决于字节序,但你可以做到
short