5.8 KiB
5.8 KiB
tags, create time
| 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 |
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
发送端:逐层封装
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)倾向于小包传输的原因。
接收端:逐层解封装
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..." 🎉
每层的动作可以概括为三步:
- 检查:校验和是否正确?mac/ip/port 是否指向自己?
- 剥离:去掉本层的 Header(必要时 Tail 如 FCS)
- 移交:将剩余的 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 段的结构与状态管理