#include#include #include inline bool is_little_endian(void){ int a = 1; return *(int *)&a == a;}// hton 和 ntoh 函数的代码完全相同uint32_t my_htonl(uint32_t hl){ if(is_little_endian()) { return *((unsigned char *)&hl ) << 24 | *((unsigned char *)&hl+1) << 16 | *((unsigned char *)&hl+2) << 8 | *((unsigned char *)&hl+3); } else { return hl; }}uint32_t my_ntohl(uint32_t nl) // 很显然 my_htonl <=> my_ntohl{ if(is_little_endian()) { return *((unsigned char *)&nl ) << 24 | *((unsigned char *)&nl+1) << 16 | *((unsigned char *)&nl+2) << 8 | *((unsigned char *)&nl+3); } else { return nl; }}int main(void){ uint32_t hl = 0x12345678; printf("%x\n", htonl(hl)); printf("%x\n", ntohl(hl)); printf("%x\n", my_htonl(hl)); printf("%x\n", my_ntohl(hl));puts("--------------------"); printf("%x\n", ntohl(123456789)); printf("%x\n", htonl(123456789)); printf("%x\n", my_ntohl(123456789)); printf("%x\n", my_htonl(123456789)); return 0;}
ntohl() 是可交换操作,所以 htonl() <-> ntohl();
这也很好理解 :
{1 2 3 4} <--A--> {4 3 2 1}
{4 3 2 1} <--B--> {1 2 3 4}
把数据看做一个抽象的点,可以看出A的操作 和 B的操作 是完全相同的
uint32_t htonl(uint32_t hostlong);uint16_t htons(uint16_t hostshort);uint32_t ntohl(uint32_t netlong);uint16_t ntohs(uint16_t netshort);// 其实是两个函数:uint32_t htonl(uint32_t hostlong);uint16_t htons(uint16_t hostshort);
背景知识:
TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节。例如上一节的UDP段格式,地址0-1是16位的源端口号,如果这个端口号是1000(0x3e8),则地址0是0x03,地址1是0xe8,也就是先发0x03,再发0xe8,这16位在发送主机的缓冲区中也应该是低地址存0x03,高地址存0xe8。
设 线性地址 布满 数轴的正半轴:
设数轴上的某段地址对于的数据为:... |0x12, 0x34, 0x56, 0x78 | 0x90, 0xAB|---->(网络中的数据流)
若把“0x12, 0x34, 0x56, 0x78 ” 这段数据看做一个int型数,它表示的数为0x12345678; 0x12所在地址为该int型数据的地址;
“0x90, 0xAB” 这段看做一个short型数据,它表示的数为0x90AB; 0x90所在地址为该short型数据的地址;
大端平台下存储 同网络中数据流的顺序... | 0x12, 0x34, 0x56, 0x78 | 0x90, 0xAB |---->
则在小端平台下存储为: ... | 0x78, 0x56, 0x34, 0x12 | 0xAB, 0x90 | ---->
0x78所在地址为 该int型数0x12345678 数据的地址
0xAB所在地址为该short型数据0x90AB 数据的地址
结论:
小端存储中,多字节数据 沿着线性地址增长的方向,从最低位 开始存放
大端存储中,多字节数据 沿着线性地址增长的方向,从最高位 开始存放 ;(这也许是小端,大端名字的由来)