如何将复杂数据类型从主机字节顺序转换为网络顺序

时间:2012-09-18 03:01:19

标签: c

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

  1. 我是否必须在每个结构的成员和成员中包含htonl(s),包括多字节的union成员?
  2. 对于工会,我可以翻译最大的工会吗?
  3. 对于枚举,我可以直接翻译它吗?
  4. 我可以只编写一个使用htonl(s)发送和接收消息的函数吗?或者我是否必须提出另一个使用ntohl(s)来接收相同消息的人?
  5. 结构

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

2 个答案:

答案 0 :(得分:3)

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

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

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

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

  5. 请注意,您的数据结构在大多数似乎合理的系统上都有各种填充孔。在结构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)

  1. 你必须以任意方式翻转任何长度超过1个字节的整数(短,整数,长整数,长整数)。
  2. 否。见下文。
  3. 否。 enum可能是任意大小,具体取决于您的平台(请参阅What is the size of an enum in C?)。
  4. 实际上,您应该使用Protocol Buffers或其他东西,而不是尝试进行所有这些转换......
  5. 工会很难处理。比如说,我将值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