网络包封装全解析:从字节流到数据帧
第一部分:网络包的构成 - “信封套信封”
网络包的构成就像一个俄罗斯套娃或一封信外面套着一个个信封,每一层都添加了本层的头部信息(有时还有尾部)。这个过程就是 封装。
我们以一个最经典的例子来说明:你的电脑(192.168.1.10)通过浏览器访问一个网站服务器(93.184.216.34)。
1. 应用层 - 原始信件
这是你真正想发送的数据。
-
内容: 一个HTTP请求,例如
GET / HTTP/1.1 Host: www.example.com ...
-
特点: 此时还没有“包”的概念,就是纯粹的应用程序数据。
2. 传输层 - 装入第一个信封(TCP)
传输层(以TCP为例)负责将应用数据分割成 manageable 的段,并确保可靠传输。
-
动作: TCP为应用数据添加一个 TCP头部。
-
TCP头部关键字段:
-
源端口: 你的浏览器随机开启的一个端口(如 52000)
-
目的端口: 服务器上Web服务的端口(如 80)
-
序列号: 用于标识这个段中数据的顺序,便于接收方重组和确认。
-
确认号: 用于确认对方发送的数据。
-
标志位: SYN, ACK, FIN, RST 等,用于控制连接状态(三次握手、四次挥手)。
-
窗口大小: 用于流量控制,告诉对方自己还能接收多少数据。
-
-
此时的数据单元称为: 段
3. 网络层 - 装入第二个信封(IP)
网络层负责将数据段从一个设备路由到另一个设备,跨越整个网络。
-
动作: IP为TCP段添加一个 IP头部。
-
IP头部关键字段:
-
源IP地址: 192.168.1.10
-
目的IP地址: 93.184.216.34
-
协议号: 6(代表 payload 是 TCP 段)
-
TTL: 生存时间,每经过一个路由器减1,防止包在网络上无限循环。
-
-
此时的数据单元称为: 包 或 数据报
4. 数据链路层 - 装入最后一个信封(以太网)
数据链路层负责在同一个本地网络内的设备之间传递数据帧。
-
动作: 为IP包添加一个 以太网头部 和 尾部。
-
以太网头部关键字段:
-
目的MAC地址: 下一跳设备的MAC地址(如果是本地通信,就是目标服务器的MAC;如果是发往外部,就是默认网关的MAC地址)。
-
源MAC地址: 你电脑网卡的MAC地址。
-
类型: 0x0800(代表 payload 是 IP 包)
-
-
以太网尾部: 主要是 帧校验序列,用于检测传输过程中是否出现比特错误。
-
此时的数据单元称为: 帧
5. 物理层 - 转换成信号
物理层不关心内容,它只负责将二进制的比特流(0和1)转换成可以在物理介质(网线、光纤、无线电波)上传输的电信号、光信号或电磁波。
总结封装过程:
应用数据 [HTTP]]
-> TCP段 [TCP头 + HTTP数据]
-> IP包 [IP头 + TCP段]
-> 以太网帧 [以太网头 + IP包 + 以太网尾]
-> 比特流
第二部分:怎么变成流发送的 - “对话与流水线”
“流”是一个抽象概念,指的是在两个应用程序之间建立的、可靠的、有序的、双向的字节序列。TCP是实现“流”感觉的关键。
1. 建立连接 - “打招呼”(三次握手)
在发送任何真实数据之前,必须先建立连接,同步序列号。
-
客户端 -> 服务器: 发送一个SYN包(标志位SYN=1),并带上一个随机初始序列号
seq=x
。 -
服务器 -> 客户端: 回复一个SYN-ACK包(SYN=1, ACK=1),并带上自己的随机初始序列号
seq=y
,以及确认号ack=x+1
。 -
客户端 -> 服务器: 发送一个ACK包(ACK=1),确认号
ack=y+1
。
至此,连接建立。双方都确认了对方的序列号起始点,可以开始传输数据。
2. 数据分割与发送 - “流水线作业”
-
MSS: 最大报文段长度。TCP会根据MSS(通常由MTU决定,见下文)将应用层传下来的大数据块(比如一个大的HTTP响应)切割成多个适合传输的TCP段。
-
滑动窗口协议: 这是实现“流”和高效率的核心。
-
发送窗口: 发送方维护一个窗口,窗口内的数据可以连续发送出去,而无需等待每一个的确认。
-
确认机制: 接收方收到数据后,会回复一个ACK包,其中的确认号表示“我期望收到的下一个字节的序列号”。这个ACK号是累积的,表示之前的所有数据都已收到。
-
窗口向前滑动: 当发送方收到一个ACK后,发送窗口就可以向前滑动,发送新的数据。
-
流量控制: 接收方通过TCP头中的“窗口大小”字段来告诉发送方自己还有多少缓冲区空间,防止发送过快导致接收方被淹没。
-
3. 有序重组 - “还原拼图”
-
由于IP网络是无连接的,每个IP包可能经过不同的路径,导致乱序、重复甚至丢失。
-
接收方的TCP协议栈会根据TCP头中的序列号,将收到的段在缓冲区中进行排序。
-
一旦排好序的、连续的数据就位,TCP就会将它们作为一个连续的字节流传递给上层的应用程序。
-
这正是“流”的 illusion 所在: 应用程序从TCP Socket 读取数据时,它感觉就像在读取一个有序的字节流(比如一个文件),完全感知不到底层被分割成多个包以及网络的各种复杂性。
4. 可靠传输 - “丢包重传”
-
发送方每发送一个段,都会启动一个重传计时器。
-
如果在一个超时时间内没有收到该段的ACK,发送方就认为这个段丢失了,会重新发送它。
-
通过序列号、确认和重传机制,TCP保证了所有数据最终都能可靠、有序地到达对端。
5. 关键角色:MTU
MTU(最大传输单元) 是数据链路层一帧所能承载的最大数据量(不包括帧头帧尾)。对于以太网,通常是1500字节。
-
TCP在切割数据时,必须保证
IP头 + TCP头 + TCP数据
不超过 MSS,而MSS = MTU - IP头 - TCP头
(通常是1460字节)。 -
如果一个IP包的大小超过了路径上某个链路的MTU,它就需要被分片。但分片会降低效率,现代网络通常通过 Path MTU Discovery 来避免分片。
总结:从包到流
-
发送端: 应用层产生数据流 -> 传输层将流切割成段,并添加序列号 -> 网络层和数据链路层将段封装成包/帧,通过物理介质发出。
-
网络中: 各个包作为独立的个体,可能经过不同的路径,经历乱序和延迟。
-
接收端: 数据链路层和网络层解封装,将段交给传输层 -> 传输层根据序列号在缓冲区中排序和重组,并通过ACK确认 -> 将重组后的原始字节流交付给应用层。
所以,“流”是TCP在传输层为我们创造的一个完美抽象,它屏蔽了底层基于“包”的、不可靠的、无序的网络的所有复杂性,让应用程序可以像操作本地文件一样进行网络通信。