2

我想使用 htonl() 和 htos() 将消息从主机字节顺序转换为网络顺序。在这个消息中,有一些复杂的定义数据类型,如结构、枚举、联合和联合中的联合。

  1. 我是否必须对每个结构的成员和成员中的成员(包括多字节的联合成员)进行 htonl(s)?
  2. 对于一个工会,我可以只翻译最大的一个吗?
  3. 对于枚举,我可以把它翻译成很长吗?
  4. 我可以只编写一个使用 htonl(s) 发送和接收消息的函数吗?还是我必须想出另一个使用 ntohl(s) 来接收相同消息的方法?

结构

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;
}
4

2 回答 2

3
  1. 您将需要适当地映射每个多字节类型。

  2. 对于联合,您需要确定哪个是联合的“活动”元素,并根据正常规则对其进行映射。您可能还需要提供一个“鉴别器”,它告诉接收代码传输了各种可能性中的哪一个。

  3. 对于枚举,您可以决定将所有此类值视为 along并相应地进行编码和解码。或者,您可以分别处理每个枚举,根据其大小处理每种类型(理论上,不同的枚举可能具有不同的大小)。

  4. 这在一定程度上取决于你接下来真正要做什么。如果您正在打包数据以通过网络传输,那么接收和发送操作就大不相同了。如果您所做的只是翻转内存中结构中的位,那么您可能会发现在大多数系统上,将htonl()函数应用于结果的结果htonl()是您首先想到的数字。如果您打算对映射(翻转)结构中的所有字节进行二进制复制,那么您可能做得不对。

请注意,在大多数可能的系统上,您的数据结构中都有各种填充孔。在结构 UN1 中,如果它是 32 位系统,几乎可以肯定在un1_c和下面的 union之间有一个填充字节;u如果它是 64 位系统,您可能会在那里填充 5 个字节。同样,在MSG结构中,您可能在 之后有 2 个填充字节,在 之后s_sid还有 3个填充字节u_char。根据. enum_i_enum

请注意,由于您没有数据类型的平台独立大小,因此您无法在 32 位和 64 位 Unix 系统之间可靠地互通。如果系统都是 Windows,那么你就可以摆脱它,因为sizeof(long) == 4在 32 位和 64 位 Windows 上都是如此。但是,在基本上所有 64 位的 Unix 变体上,sizeof(long) == 8. 因此,如果跨平台工作是一个问题,您必须担心这些尺寸以及填充。调查<inttypes.h>标头中的类型,例如uint16_tuint32_t

您应该简单地在所有主机上进行相同的打包,小心地将各种值的字节复制到字符缓冲区中的适当位置,然后通过线路发送并通过反向编码解包。

还要检查 Google 的Protocol Buffers是否会明智地为您完成这项工作;它可能会为您节省相当多的痛苦和悲伤。

于 2012-09-18T04:04:44.933 回答
2
  1. 您必须对长度超过 1 个字节的任何整数进行字节序翻转(short、int、long、long long)。
  2. 不,见下文。
  3. 编号enum可能是任何大小,具体取决于您的平台(请参阅C 中枚举的大小是多少?)。
  4. 实际上,您应该只使用协议缓冲区或其他东西,而不是尝试进行所有这些转换......

工会很难处理。例如,我将值 0x1234 存储在大端short的 a中。union {short; long;}然后,联合包含字节12 34 00 00,因为短占用联合的低两个字节。如果你对 进行字节序翻转long,你会得到00 00 34 12,这会产生short0x0000。如果你对 进行字节序翻转short,你会得到34 12 00 00. 我不确定您认为哪一个是正确的,但很明显您有问题。

更典型short的是在这样的联合中有两个 s,一个short是低半字,另一个是高半字。哪一个取决于字节顺序,但你可以这样做

union {
    #ifdef LITTLE_ENDIAN
    uint16_t s_lo, s_hi;
    #else
    uint16_t s_hi, s_lo;
    #endif
    uint32_t l;
};
于 2012-09-18T03:33:25.737 回答