网络字节序与主机字节序的转换函数实践
CPU向内存保存数据的方式有2种,这意味着CPU解析数据的方式也分为2种:
♦ 大端序(big endian):高位字节存放到低位地址;
♦ 小端序(little endian):高位字节存放到高位地址。
不同的机器主机字节序不相同,与CPU设计有关,数据的顺序是由CPU决定的,而与操作系统无关。我们把某个给定系统所用的字节序称为主机字节序(host byte order)。比如x86系列CPU都是little-endian的字节序。由于这个原因不同体系结构的机器之间无法通信,所以要转换成一种约定的数序,也就是网络字节顺序,采用大端序的排序方法。
Linux socket网络编程中,经常会使用下面四个C标准库函数进行字节序间的转换:
1 2 3 4 5 | #include <arpa/inet.h> uint32_t htonl(uint32_t hostlong); //把uint32_t类型从主机序转换到网络序 uint16_t htons(uint16_t hostshort); //把uint16_t类型从主机序转换到网络序 uint32_t ntohl(uint32_t netlong); //把uint32_t类型从网络序转换到主机序 uint16_t ntohs(uint16_t netshort); //把uint16_t类型从网络序转换到主机序 |
如果需要对64位类型数据进行主机字节序与网络字节序的转换,没有现成系统API可用,可以通过下面两种方法进行转换:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | //主机序转网络序 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; } } |
根据联合体的特性:联合中所有成员引用的是内存中相同的位置,其长度为最长成员的长度:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | 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; } } |
使用编译器内置函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #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 |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix