127 lines
5.8 KiB
Markdown
127 lines
5.8 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, state=ESTABLISHED
|
|||
|
|
TCP->>IP: 传递 Segment (daddr=x.x.x.x, sport=1234, dport=80)
|
|||
|
|
Note over IP: 查路由表 → 下一跳网关
|
|||
|
|
IP->>ETH: 传递 Packet (dst_mac=?, src_ip=192.168.1.100, dst_ip=93.184.216.34)
|
|||
|
|
Note over ETH: ARP 解析 → dst_mac=aabb.ccdd.eeff
|
|||
|
|
ETH->>ETH: 拼接 源MAC+目的MAC+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)。
|
|||
|
|
|
|||
|
|
> [!note] MTU = 1500 是什么意思?
|
|||
|
|
> MTU(Maximum Transmission Unit)是链路层允许的最大 Payload 大小,即 IP 包的最大体积。超过会被分片(Fragmentation),但分片会降低性能且增加丢包风险——这也是现代应用层(如 TLS、gRPC)倾向于小包传输的原因。
|
|||
|
|
|
|||
|
|
## 接收端:逐层解封装
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
sequenceDiagram
|
|||
|
|
participant ETH as 网卡收到 Frame
|
|||
|
|
participant IP as 剥离以太网头
|
|||
|
|
participant TCP as 剥离 TCP 头
|
|||
|
|
participant App as 提取 HTTP 数据
|
|||
|
|
|
|||
|
|
ETH->>IP: 校验 FCS OK → 取 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 对应 | 关键标识字段 |
|
|||
|
|
|----------|-----|-------------|-------------|
|
|||
|
|
| 数据 (Data) | 报文段 | 应用层消息 | — |
|
|||
|
|
| 段 (Segment) | TCP Segment | 传输层 PDU | 源端口 + 目的端口 |
|
|||
|
|
| 包 (Packet) | IP Datagram | 网络层 PDU | 源 IP + 目的 IP + Protocol |
|
|||
|
|
| 帧 (Frame) | Ethernet Frame | 链路层 PDU | 源 MAC + 目的 MAC + EtherType |
|
|||
|
|
| 比特流 (Bits) | Bitstream | 物理层信号 | 电信号 / 光脉冲 |
|
|||
|
|
|
|||
|
|
## NAT 场景下的封装变化
|
|||
|
|
|
|||
|
|
NAT 路由器修改了中间层的地址:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
发送端内部主机 NAT 路由器 外部 Web 服务器
|
|||
|
|
┌──────────────────────┐ ┌──────────────────────┐ ┌──────────────────────┐
|
|||
|
|
│ Src IP: 10.0.0.5:45678 │ │ SNAT: 改写 Src IP │ │ Dst IP: 203.0.113.1:80 │
|
|||
|
|
│ Dst IP: 203.0.113.1:80 │ → │ │ → │ │
|
|||
|
|
│ │ │ → Src IP: 8.8.8.8:45678 │ │ ← Src IP: 203.0.113.1:80 │
|
|||
|
|
│ │ │ → Dst IP: 203.0.113.1:80 │ │ → Src IP: 10.0.0.5:45678 │
|
|||
|
|
└──────────────────────┘ └──────────────────────┘ └──────────────────────┘
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
注意:NAT 不触碰应用层载荷,只是在中途修改了 IP 和 TCP 端口。这也意味着 **传输层的 Checksum 需要重新计算**。
|
|||
|
|
|
|||
|
|
## 关联笔记
|
|||
|
|
|
|||
|
|
- [[hhs/NETWORK/OSI与TCP-IP模型对比]] — 分层模型的起源与对比
|
|||
|
|
- [[hhs/NETWORK/IPv4协议详解]] — IP 包的详细格式
|
|||
|
|
- [[hhs/NETWORK/TCP状态机详解]] — TCP 段的结构与状态管理
|