通俗易懂说字节序,大小端,网络序和主机序(2)htonl和ntohl 源码实现
上个博客:
通俗易懂说字节序,大小端,网络序和主机序
https://blog.csdn.net/lqy971966/article/details/93602177
1. htonl 内核源码实现
系统函数htonl在头文件 netinet/in.h中
# if __BYTE_ORDER == __BIG_ENDIAN
/* The host byte order is the same as network byte order,
so these functions are all just identity.
如果系统是 BIG_ENDIAN 那么网络字节序(大端)和运算字节序是一致的 不用处理
*/
# define ntohl(x) (x)
# define ntohs(x) (x)
# define htonl(x) (x)
# define htons(x) (x)
# else
# if __BYTE_ORDER == __LITTLE_ENDIAN
/*
如果是 LITTLE_ENDIAN 那么需要进行 __bswap_32() 操作
*/
# define ntohl(x) __bswap_32 (x)
# define ntohs(x) __bswap_16 (x)
# define htonl(x) __bswap_32 (x)
# define htons(x) __bswap_16 (x)
# endif
# endif
#endif
1.1 注意:这里有判断,如果本身内存中存储的就是网络序,那就不转
如:
ip地址存的时候是网络序(大端),进程间通信转序处理
这里跳过了,都是大端,跳过
,
__bswap_32() 在 gcc 中实现,位于bits/byteswap.h(不要直接引用此文件;使用 byteswap.h 中的 bswap32 代替):
/* Swap bytes in 32 bit value. */
#define __bswap_constant_32(x) \
((((x) & 0xff000000u) >> 24) | (((x) & 0x00ff0000u) >> 8) | \
(((x) & 0x0000ff00u) << 8) | (((x) & 0x000000ffu) << 24))
1.1 __bswap_32()例子
例如: 0x1234
二进制: 00000001 00000010 00000011 00000100
((x) & 0xff000000u) 结果是: 0x1000 >> 24 就是 0x0001
((x) & 0x00ff0000u) 结果是: 0x0200 >> 8 就是 0x0020
((x) & 0x0000ff00u) 结果是: 0x0030 << 8 就是 0x0300
((x) & 0x000000ffu) 结果是: 0x0004<< 24 就是 0x4000
然后进行 | 或操作 就是 0x4321
2. ntohl
同理 htonl 一样处理
3. 解释了为什么项目中的都用htonl 问题
- 项目中都使用 htonl ,系统发消息使用 htonl ,另外接收的时候也用 htonl。
因为 htonl 和 ntohl 仅仅名字不同,实现是一样的,
如上面所示:
都是 __bswap_32
define htonl(x) __bswap_32 (x)
define ntohl(x) __bswap_32 (x)
4. long long类型(8字节)字节序转换
上边的几个函数有对32位和16位的数值转换,但是如果是64位的就没有现成的API可以调用了
4.1 方法1: 使用位移
//主机序转网络序
unsigned long long htonll(unsigned long long val)
{
if(__BYTE_ORDER == __LITTLE_ENDIAN) {
return (((unsigned long long )htonl((int)((val << 32) >> 32))) << 32) | (unsigned int)htonl((int)(val >> 32));
}
else if (__BYTE_ORDER == __BIG_ENDIAN) {
return val;
}
}
//网络序转主机序
unsigned long long ntohll(unsigned long long val)
{
if (__BYTE_ORDER == __LITTLE_ENDIAN) {
return (((unsigned long long )ntohl((int)((val << 32) >> 32))) << 32) | (unsigned int)ntohl((int)(val >> 32));
}
else if (__BYTE_ORDER == __BIG_ENDIAN) {
return val;
}
}
4.2 方法2:使用联合体union
边使用联合体的特性:联合体中所有成员引用的是内存中相同的位置,其长度为最长成员的长度。
typedef struct {
unsigned int u32_h;
unsigned int u32_l;
}Int64_t;
typedef union {
unsigned long long u64;
Int64_t st64;
}Convert64_t;
//主机序转网络序
unsigned long long htonll(unsigned long long val)
{
if (__BYTE_ORDER == __LITTLE_ENDIAN){
Convert64_t box_in, box_out;
box_in.u64 = val;
box_out.st64.u32_h = htonl(box_in.st64.u32_l);
box_out.st64.u32_l = htonl(box_in.st64.u32_h);
return box_out.u64;
}
else if (__BYTE_ORDER == __BIG_ENDIAN) {
return val;
}
}
//网络序转主机序
unsigned long long ntohll(unsigned long long val)
{
if (__BYTE_ORDER == __LITTLE_ENDIAN){
Convert64_t box_in, box_out;
box_in.u64 = val;
box_out.st64.u32_h = ntohl(box_in.st64.u32_l);
box_out.st64.u32_l = ntohl(box_in.st64.u32_h);
return box_out.u64;
}
else if(__BYTE_ORDER == __BIG_ENDIAN){
return val;
}
}
4.3 方法3:使用编译器内置函数
#ifdef WIN32
#define ntohll(x) _byteswap_uint64 (x)
#define htonll(x) _byteswap_uint64 (x)
#else
#if __BYTE_ORDER == __BIG_ENDIAN
#define ntohll(x) (x)
#define htonll(x) (x)
#else
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define ntohll(x) __bswap_64 (x)
#define htonll(x) __bswap_64 (x)
#endif
#endif
#endif