大端、小端与网络字节序:你必须真正弄懂的底层知识

大端、小端与网络字节序:你必须真正弄懂的底层知识

(为什么要转换?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 都能通信,必须在发收数据时做字节序转换。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值