Files
cs-note/hhs/NETWORK/01-基础概念/02-数据封装与解封装.md
T
2026-05-27 00:12:00 +08:00

8.6 KiB
Raw Blame History

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, 连接已建立"
    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。

接收端:逐层解封装

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 路由器修改了中间层的地址:

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 强制
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 层(会话/表示层) 工作,夹在应用层和传输层之间:

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)。

关联笔记