网络字节序转换函数的实现原理
网络字节序转换的操作函数有这几个 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;
}