网络字节序转换的操作函数有这几个 ntohs()htons()ntohl()htonl() ,不同平台下这几个函数依赖的头文件各有千秋,而且还没有 64位的转换接口。写跨平台代码的时候,为了省掉这些差异,干脆自己来实现一套,这里做个笔记。

1. 知识要点

  • 大端模式(Big-endian):是指数据的高字节,保存在内存的低地址中,而数据的低字节,保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放。
  • 小端模式(Little-endian):是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内在的低地址中,这种存储模式将地址的高低和数据位 权有效结合起来,高地址部分权值高,低地址部分权值低,和我们的逻辑方法一致。
  • 主机字节序:是指 整数(如16位,32位,64位这些类型的整数) 在内存中保存的顺序,不同的 CPU 可能使用不同的字节序类型,现下绝大多数使用小端模式。
  • 网络字节序:网络字节序是使用大端的模式。

2. 判断主机字节序

参看 Linux 内核的源代码,是类似于这样实现的:

static union
{
    x_char_t   xct_order[4];
    x_uint32_t xut_order;
} xhost_order = { { 'L', '?', '?', 'B' } };

#define X_IS_LITTLE_ENDIAN ('L' == (x_char_t)xhost_order.xut_order)
#define X_IS_BIG_ENDIAN    ('B' == (x_char_t)xhost_order.xut_order)

3. 自己实现的网络字节序转换函数

代码如下所示:

typedef char                 x_char_t;
typedef unsigned short       x_uint16_t;
typedef unsigned int         x_uint32_t;
typedef unsigned long long   x_uint64_t;

/**
 * @brief 用于进行判断主机字节序的联合体。
 * @note
 * 小端:低地址存放低字节,高地址存放高字节;
 * 大端:高地址存放低字节,低地址存放高字节;
 * 网络字节序是大端。
 */
static union
{
    x_char_t   xct_order[4];
    x_uint32_t xut_order;
} xhost_order = { { 'L', '?', '?', 'B' } };

#define X_IS_LITTLE_ENDIAN ('L' == (x_char_t)xhost_order.xut_order)
#define X_IS_BIG_ENDIAN    ('B' == (x_char_t)xhost_order.xut_order)

/**********************************************************/
/**
 * @brief 字节序转换:16 位整数从 网络字节序 转成 主机字节序。
 */
x_uint16_t vx_ntohs(x_uint16_t xut_short)
{
    if (X_IS_LITTLE_ENDIAN)
        return ((xut_short << 8) | (xut_short >> 8));
    return xut_short;
}

/**********************************************************/
/**
 * @brief 字节序转换:16 位整数从 主机字节序 转成 网络字节序。
 */
x_uint16_t vx_htons(x_uint16_t xut_short)
{
    if (X_IS_LITTLE_ENDIAN)
        return ((xut_short << 8) | (xut_short >> 8));
    return xut_short;
}

/**********************************************************/
/**
 * @brief 字节序转换:32 位整数从 网络字节序 转成 主机字节序。
 */
x_uint32_t vx_ntohl(x_uint32_t xut_long)
{
    if (X_IS_LITTLE_ENDIAN)
        return (((xut_long             ) << 24) |
                ((xut_long & 0x0000FF00) <<  8) |
                ((xut_long & 0x00FF0000) >>  8) |
                ((xut_long             ) >> 24));
    return xut_long;
}

/**********************************************************/
/**
 * @brief 字节序转换:32 位整数从 主机字节序 转成 网络字节序。
 */
x_uint32_t vx_htonl(x_uint32_t xut_long)
{
    if (X_IS_LITTLE_ENDIAN)
        return (((xut_long             ) << 24) |
                ((xut_long & 0x0000FF00) <<  8) |
                ((xut_long & 0x00FF0000) >>  8) |
                ((xut_long             ) >> 24));
    return xut_long;
}

/**********************************************************/
/**
 * @brief 字节序转换:64 位整数从 网络字节序 转成 主机字节序。
 */
x_uint64_t vx_ntohll(x_uint64_t xult_llong)
{
    if (X_IS_LITTLE_ENDIAN)
        return (((xult_llong                     ) << 56) |
                ((xult_llong & 0x000000000000FF00) << 40) |
                ((xult_llong & 0x0000000000FF0000) << 24) |
                ((xult_llong & 0x00000000FF000000) <<  8) |
                ((xult_llong & 0x000000FF00000000) >>  8) |
                ((xult_llong & 0x0000FF0000000000) >> 24) |
                ((xult_llong & 0x00FF000000000000) >> 40) |
                ((xult_llong                     ) >> 56));
    return xult_llong;
}