通俗易懂说字节序,大小端,网络序和主机序(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 问题

  1. 项目中都使用 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

参考

https://www.cnblogs.com/pied/p/5371231.html

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值