187 lines
8.6 KiB
Markdown
187 lines
8.6 KiB
Markdown
---
|
||
tags: [计算机网络, 数据封装, 网络分层]
|
||
create time: 2026-05-17 22:20
|
||
---
|
||
|
||
# 数据封装与解封装
|
||
|
||
## 概述
|
||
|
||
数据在发送端逐层加上头部(Header),就像装快递盒子:最里层是商品,外面一层一层套纸箱、贴面单、打托盘。到达接收端后,每一层剥掉自己的那层包装,只把原始内容交给上层处理。这个"套娃+拆箱"的过程就是 **封装(Encapsulation)** 和 **解封装(Decapsulation)**。
|
||
|
||
> [!QUESTION] 为什么每层都要加头?
|
||
> 因为每层需要知道如何管理自己的事务——传输层关心连接状态,网络层关心路由寻址,链路层关心物理介质。这些元信息必须跟在数据前面,让每一层都能正确解读并转发。
|
||
|
||
## 数据包尺寸变化轨迹
|
||
|
||
以发送一个 HTTP GET 请求为例,追踪尺寸变化:
|
||
|
||
| 层级 | 操作 | 数据结构 | 新增首部大小 | 累计大小 |
|
||
|------|------|---------|-------------|---------|
|
||
| 应用层 | HTTP GET 报文 | `GET / HTTP/1.1\r\nHost: example.com\r\n\r\n` | — | ~40 bytes |
|
||
| 传输层 (TCP) | + TCP Header | Segment | 20 bytes | ~60 bytes |
|
||
| 网络层 (IP) | + IP Header | Packet | 20 bytes (IPv4) | ~80 bytes |
|
||
| 链路层 (Ethernet) | + Eth Header + FCS | Frame | 14 + 4 = 18 bytes | ~98 bytes |
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
H["HTTP GET 40B"] --> T["+ TCP Header 20B<br/>Segment 60B"]
|
||
T --> I["+ IP Header 20B<br/>Packet 80B"]
|
||
I --> E["+ Ethernet HDR 14B + FCS 4B<br/>Frame 98B"]
|
||
E --> B["98 个 Bit 在网线上传输"]
|
||
|
||
style H fill:#DDA0DD
|
||
style T fill:#FFD700
|
||
style I fill:#87CEEB
|
||
style E fill:#98FB98
|
||
```
|
||
|
||
## 发送端:逐层封装
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant App as "HTTP 应用"
|
||
participant TCP as "TCP Socket"
|
||
participant IP as "网络层 IP"
|
||
participant ETH as "以太网驱动"
|
||
|
||
App->>TCP: "write GET / HTTP/1.1..."
|
||
Note over TCP: "分配 seq,ack, 连接已建立"
|
||
TCP->>IP: "Segment, daddr=x.x.x.x, sport=1234, dport=80"
|
||
Note over IP: "查路由表, 下一跳网关"
|
||
IP->>ETH: "Packet, src_ip=192.168.1.100, dst_ip=93.184.216.34"
|
||
Note over ETH: "ARP 解析, dst_mac=aabb.ccdd.eeff"
|
||
ETH->>ETH: "拼接 srcMAC,dstMAC,EtherType,Payload,FCS"
|
||
ETH-->>App: "sendto 返回, 帧已发出"
|
||
```
|
||
|
||
### Ethernet II 帧完整结构
|
||
|
||
```
|
||
Offset Size Field 说明
|
||
────── ──── ───────────────── ─────────────────────────────
|
||
0 6 Dest MAC Address 目标 MAC 地址
|
||
6 6 Source MAC Address 源 MAC 地址
|
||
12 2 EtherType 0x0800=IPv4 / 0x86DD=IPv6 / 0x0806=ARP
|
||
14 N Payload IP 包(最大 1500 字节)
|
||
14+N 4 FCS CRC-32 帧校验序列(接收端自动验证)
|
||
```
|
||
|
||
总长度范围:**64 ~ 1518 bytes**(不含 VLAN Tag;有 VLAN Tag 则上限 1522)。
|
||
|
||
> [!tip] 为什么有最小 64 字节的限制?
|
||
> 以太网使用 CSMA/CD 检测冲突,发送方需要在发完帧之前检测到碰撞信号。64 字节是为了保证在最大网络直径内,信号能往返一次。如果 IP 包太小导致帧不够 64 字节,链路层会自动填充 **Padding** 补齐到最小长度。接收端根据 IP 头部的 "Total Length" 字段识别真实载荷边界,剥离多余的填充。
|
||
|
||
> [!note] MTU = 1500 是什么意思?
|
||
> MTU(Maximum Transmission Unit)是链路层允许的最大 Payload 大小,即 IP 包(头部 + 载荷)的最大体积。超过 1500 字节的 IP 包会被路由器**分片(Fragmentation)**——拆成多个小片段分别传输,接收端再重组。
|
||
> 分片的代价:任何一个分片丢失都会导致整个包重传;每片都需要独立的 IP 头,浪费带宽。因此 TCP 在三次握手时会协商 **MSS**(Maximum Segment Size)来主动避分片,而 UDP 应用(如 DNS、QUIC)则通过程序层面控制载荷大小。更底层的 **PMTU Discovery** 机制还能动态探测路径上最小的 MTU。
|
||
|
||
## 接收端:逐层解封装
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant ETH as "网卡收到 Frame"
|
||
participant IP as "剥离以太网头"
|
||
participant TCP as "剥离 TCP 头"
|
||
participant App as "提取 HTTP 数据"
|
||
|
||
ETH->>IP: "FCS 校验通过, EtherType=0x0800, 剥离以太网头"
|
||
IP->>TCP: "IP Checksum 正确, Protocol=6 TCP, 剥离 IP 头"
|
||
TCP->>App: "按 Sequence Number 重组, 剥离 TCP 头"
|
||
App->>App: "解析 GET / HTTP/1.1..."
|
||
```
|
||
|
||
每层的动作可以概括为三步:
|
||
|
||
1. **检查**:校验和是否正确?mac/ip/port 是否指向自己?
|
||
2. **剥离**:去掉本层的 Header(必要时 Tail 如 FCS)
|
||
3. **移交**:将剩余的 Payload 交给上层协议处理
|
||
|
||
## 各层 PDU 对照表
|
||
|
||
| OSI 层 | PDU 名称 | TCP/IP 对应 | 关键标识字段 |
|
||
|--------|---------|-------------|-------------|
|
||
| 应用层 | 消息 (Message) | 应用层数据 | HTTP 请求行、DNS 报文等 |
|
||
| 传输层 | 段/数据报 (Segment / Datagram) | 传输层 PDU | 源端口 + 目的端口 |
|
||
| 网络层 | 包 (Packet / Datagram) | 网络层 PDU | 源 IP + 目的 IP + Protocol |
|
||
| 链路层 | 帧 (Frame) | 链路层 PDU | 源 MAC + 目的 MAC + EtherType |
|
||
| 物理层 | 比特流 (Bits) | 物理层信号 | 电信号 / 光脉冲 / 无线电波 |
|
||
|
||
> [!QUESTION] TCP 叫 Segment,UDP 叫什么?
|
||
> UDP 的 PDU 通常叫 **Datagram**(数据报),因为它不建立连接、不做分段重组,每个数据报独立发送。所以同一层可以有不同的 PDU 名称,取决于使用的传输协议。
|
||
|
||
## NAT 场景下的封装变化
|
||
|
||
NAT 路由器修改了中间层的地址:
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant Host as "内部主机"
|
||
participant NAT as "NAT 路由器"
|
||
participant Server as "外部服务器"
|
||
|
||
Host->>NAT: "Src=10.0.0.5:45678 Dst=203.0.113.1:80"
|
||
Note over NAT: "SNAT 改写源地址"
|
||
NAT->>Server: "Src=8.8.8.8:45678 Dst=203.0.113.1:80"
|
||
Server->>NAT: "Src=203.0.113.1:80 Dst=8.8.8.8:45678"
|
||
Note over NAT: "查 NAT 表, 反向翻译"
|
||
NAT->>Host: "Src=203.0.113.1:80 Dst=10.0.0.5:45678"
|
||
```
|
||
|
||
NAT **不触碰应用层载荷**,只修改 IP 头和 TCP/UDP 端口。但这也意味着**传输层的 Checksum 需要重新计算**,因为 TCP/UDP 的校验和覆盖了伪首部(Pseudo Header)中的源/目的 IP 地址。
|
||
|
||
## TCP vs UDP:封装有什么不同?
|
||
|
||
两者都在传输层,但封装风格截然不同:
|
||
|
||
| 对比项 | TCP | UDP |
|
||
|--------|-----|-----|
|
||
| 首部大小 | 最小 20 字节,最大 60 字节(含 Options) | 固定 8 字节 |
|
||
| 连接状态 | 三次握手建连接,携带 seq/ack 等状态 | 无连接,每个数据报独立 |
|
||
| 分段重组 | 传输层自动分段和重组 | 不分段,超过 MTU 则由 IP 层分片 |
|
||
| 校验和 | 强制(IPv4/IPv6) | IPv4 可选,IPv6 强制 |
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
subgraph "TCP Segment"
|
||
A["TCP Header 20-60B<br/>SrcPort DstPort Seq Ack Flags..."] --> B["Payload"]
|
||
end
|
||
subgraph "UDP Datagram"
|
||
C["UDP Header 8B<br/>SrcPort DstPort Len Chksum"] --> D["Payload"]
|
||
end
|
||
|
||
style A fill:#FFD700
|
||
style C fill:#FFA07A
|
||
```
|
||
|
||
> [!QUESTION] UDP 头只有 8 字节,是不是"更好"?
|
||
> 更小的头部确实开销更低,但你失去的是可靠传输、流量控制、拥塞控制等保障。选择哪个取决于业务场景:文件传输用 TCP,游戏/视频/QUIC 用 UDP + 应用层自建可靠性。
|
||
|
||
## TLS 封装:在哪一层"套娃"?
|
||
|
||
TLS(Transport Layer Security)常被认为是"应用层协议",但它实际上在 **OSI 第 5-6 层(会话/表示层)** 工作,夹在应用层和传输层之间:
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
subgraph "HTTPS = HTTP + TLS + TCP"
|
||
H["HTTP 报文"] --> T["TLS 加密<br/>+ TLS Record Header 5B"]
|
||
T --> C["TCP Segment"]
|
||
C --> I["IP Packet"]
|
||
end
|
||
|
||
style H fill:#DDA0DD
|
||
style T fill:#FF6347
|
||
style C fill:#FFD700
|
||
style I fill:#87CEEB
|
||
```
|
||
|
||
关键点:TLS Record 协议会在 HTTP 载荷前添加 **5 字节的 Record Header**(ContentType + Version + Length),然后整个 TLS Record 作为 TCP 的 Payload 继续向下封装。所以 HTTPS 比 HTTP 多了 TLS 的封装开销(握手阶段更大,数据阶段约 ~40 字节 overhead)。
|
||
|
||
## 关联笔记
|
||
|
||
- [[hhs/NETWORK/01-基础概念/01-OSI与TCP-IP模型对比]] — 分层模型的起源与对比
|
||
- [[hhs/NETWORK/03-网络层/01-IPv4首部与分段重组]] — IP 包的详细格式与分片机制
|
||
- [[hhs/NETWORK/04-传输层/01-TCP段结构与状态机]] — TCP 段的结构与状态管理
|
||
- [[hhs/NETWORK/04-传输层/06-UDP协议与SCTP]] — UDP 的无连接封装特点
|
||
- [[hhs/NETWORK/03-网络层/08-NAT原理与应用]] — NAT 的详细原理与配置
|