文章目录
大端、小端与网络字节序:你必须真正弄懂的底层知识
(为什么要转换?TCP/IP 为什么必须用大端?)
在网络编程中,无论你写 C、C++ 还是 Go、Java,总会遇到两个魔法函数:
htons() // host to network short (16位)
htonl() // host to network long (32位)
刚开始写 socket 通信的人经常问:
“为什么端口要用 htons?
为什么 IP 地址要转换成大端?
为什么服务器不能直接使用机器自己的字节序?”
要回答这些问题,我们必须从“字节序”说起。
一、什么是字节序?
字节序(Endianness)描述的是:
在内存中,一个多字节的数据(如 int、short)应该如何以字节的形式进行排列。
例如数字:
0x12345678
在内存中可能有两种排列方式:
1. 大端(Big-Endian)
高位字节存到低地址,低位字节存到高地址:
地址 ↑
0x12
0x34
0x56
0x78
就像我们写数字一样,从“高位到低位”阅读。
2. 小端(Little-Endian)
低位字节存到低地址,高位字节存到高地址:
地址 ↑
0x78
0x56
0x34
0x12
Intel x86 系列使用小端字节序。
通俗一点的理解:
- 大端:像我们看书,从左到右,高位在左
- 小端:像我们写十六进制数时从右往左存储,低位在左
两者谁对谁错?都对。只是习惯不同。
二、不同 CPU 架构字节序不一致
不同 CPU 在内存中摆放字节的顺序是不一样的:
| 架构 | 字节序 |
|---|---|
| x86 / x86_64(主流 PC) | 小端 |
| ARM(安卓、iPhone、多数嵌入式) | 默认小端,但可配置 |
| PowerPC | 大端 |
| SPARC | 大端 |
| MIPS | 大端或小端 |
问题来了:
如果两台字节序不同的机器要通信,会发生什么?
例如:
- A 是小端机器
- B 是大端机器
A 想把端口号 8000 (0x1F40) 发送给 B
在 A 内存中:
40 1F (小端)
B 会把它当成:
0x401F = 16415
这就完全错了!
所以必须统一标准。
三、网络通信必须统一字节序
IETF(互联网协议标准)规定:
所有传输到网络上的多字节整数,必须使用大端字节序(Network Byte Order)。
也就是:
网络字节序 = 大端字节序
于是:
- 不管你的 CPU 是小端还是大端
- 上网之前必须转换成大端
- 从网络收到后要转换回本地字节序
这样世界上任何两台主机都能正常通信。
四、htons / htonl / ntohs / ntohl 的作用
这些函数就是专门用来做“字节序转换”的。
1. htons() —— host to network short
把本机字节序的 16 位整数转换成网络(大端)字节序。
常用于端口号:
addr.sin_port = htons(8000);
2. htonl() —— host to network long
把本机字节序的 32 位整数转换成网络(大端)字节序。
常用于 IPv4 地址:
addr.sin_addr.s_addr = htonl(ip_value);
3. ntohs() / ntohl() —— 接收数据时转回本地字节序
uint16_t port = ntohs(addr.sin_port);
uint32_t ip = ntohl(addr.sin_addr.s_addr);
4. 在大端机器上这些函数“什么都不做”
因为它已经是网络字节序(大端),所以直接返回原值。
而在小端机器上,它们会交换字节顺序。
五、为什么字符串 IP 不需要转换?
例如:
"127.0.0.1"
这是一个 字符数组,按字节处理,不存在高位和低位的概念,所以不需要大小端转换。
大小端只影响整数类型,例如:
- short(16 位)
- int(32 位)
- long long(64 位)
六、为什么服务器一定要用 htons/htonl?
因为:
- Linux 内核使用的是大端网络协议
- 网络协议规定:头部字段必须是大端(源端口、目的端口、序列号、确认号、IP 等)
- TCP/IP 协议无法适配每个 CPU 的字节序
- 源代码需要跨平台运行(不同 CPU 字节序都能工作)
- 用户无需知道 CPU 是大端还是小端,统一转换即可
简单说:
这是整个互联网能正常通信的关键一步。
如果不做转换:
- 端口号会错
- IP 地址会错
- TCP 头部字段会错
- 数据包无法解析,连接无法建立
网络编程几乎就不能用了。
七、你在写服务器时什么时候必须转换?
只要涉及 “整数 → 网络数据” 的地方:
- IP 地址(转换成整数时)
- 端口号
- TCP/UDP/ICMP协议头
- 自己定义的网络协议里包含整数字段
基本规则:
发送时:本机 → 网络(htons/htonl)
接收时:网络 → 本机(ntohs/ntohl)
八、总结(最容易记住的版本)
-
不同 CPU 字节序不同
- x86 小端
- 有些 ARM 小端
- 一些 CPU 大端
-
网络必须统一字节序 → 大端(Big Endian)
-
发送数据之前必须用 htons/htonl 转换
-
接收数据之后必须用 ntohs/ntohl 转换
-
字符串不需要转换,整数才需要
一句话总结:
大小端是 CPU 的内部习惯,而网络字节序是全世界的统一标准。
为了让所有 CPU 都能通信,必须在发收数据时做字节序转换。

6484

被折叠的 条评论
为什么被折叠?



