vault backup: 2026-05-27 00:12:00

This commit is contained in:
hhs
2026-05-27 00:12:00 +08:00
parent 838e5e7824
commit 91b4fe3f36
9 changed files with 1116 additions and 180 deletions
@@ -74,16 +74,16 @@ graph BT
```mermaid
flowchart LR
A["OSI 七层模型<br/>ISO 标准"] --> B{谁先落地?}
B -->|"1970s 末"| C["TCP/IP 已部署在 ARPANET"]
B -->|"1980s 后"| D["OSI 标准太多太复杂"]
B -->|"1983 Flag Day"| C["TCP/IP 正式接管 ARPANET"]
B -->|"标准滞后"| D["OSI 标准太多太复杂"]
C --> E["事实标准 ✅"]
D --> E
style E fill:#98FB98,color:#000
```
- **先发优势**:ARPANET / Internet 先用上了 TCP/IP,基础设施已经建成
- **先发优势**:1983 年 1 月 1 日(Flag Day),ARPANET 强制切换到 TCP/IP,基础设施已建成,生态不可逆转
- **简单粗暴**:TCP/IP 只关心能不能跑通,不搞复杂的标准化流程
- **开源免费**:Berkeley BSD Unix 内嵌 TCP/IP 协议栈,免费分发
- **开源免费**:Berkeley BSD Unix 内嵌 TCP/IP 协议栈(1983 年 4.2BSD),免费分发
- **OSI 的反面教材**:层层审批导致协议膨胀(X.400 邮件、X.25 分组交换),开发者根本懒得用
## 五层教学模型
@@ -92,10 +92,10 @@ flowchart LR
```mermaid
flowchart TD
A["应用层 — Application<br/>HTTP DNS SMTP SSH FTP SMTP POP3 IMAP WebSocket"] --> T["传输层 — Transport<br/>TCP UDP SCTP"]
T --> N["网络层 — Internet<br/>IP IPv6 ICMP ARP NAT"]
N --> D["链路层 — Link<br/>Ethernet Wi-Fi 802.11 VLAN HDLC PPP"]
D --> P["物理层 — Physical<br/>双绞线 光纤 无线电 IEEE 802.15"]
A["应用层 — Application<br/>HTTP, DNS, SMTP, SSH, FTP, POP3, IMAP, WebSocket"] --> T["传输层 — Transport<br/>TCP, UDP, SCTP"]
T --> N["网络层 — Internet<br/>IP, IPv6, ICMP, ARP, NAT"]
N --> D["链路层 — Link<br/>Ethernet, Wi-Fi, 802.11, VLAN, HDLC, PPP"]
D --> P["物理层 — Physical<br/>双绞线, 光纤, 无线电, IEEE 802.15"]
style A fill:#DDA0DD
style T fill:#FFD700
style N fill:#87CEEB
@@ -153,7 +153,19 @@ sequenceDiagram
> 每一层只知道自己那一层的 header 是什么格式,对其他层的内容一无所知。这就是**层间透明**原则。
> [!QUESTION] 如果每一层都要加头部,开销岂不是很大?
> 没错。一个 1 字节的应用层数据,经过 TCP/IP/以太网三层封装后,可能膨胀到 50+ 字节(TCP 头 20B + IP 头 20B + 以太网帧头帧尾 18B)。这就是为什么对小数据量、低延迟场景,业界会选择 **UDP**(头部仅 8 字节)甚至 **QUIC**(合并传输层与加密层)来减少开销。
> 没错。一个 1 字节的应用层数据,经过 TCP/IP/以太网三层封装后,可能膨胀到 58 字节(TCP 头 20B + IP 头 20B + 以太网帧头帧尾 18B)。这就是为什么对小数据量、低延迟场景,业界会选择 **UDP**(头部仅 8 字节)甚至 **QUIC**(合并传输层与加密层)来减少开销。
### MTU 与 MSS:分层边界如何约束数据大小
分层不只是理论模型,它直接决定了每层能传多大的数据:
| 概念 | 所在层 | 含义 | 典型值 |
|------|--------|------|--------|
| **MTU**(Maximum Transmission Unit) | 链路层 | 一个帧能承载的最大数据量 | 以太网默认 **1500B** |
| **MSS**(Maximum Segment Size) | 传输层 | 一个 TCP 段能承载的最大应用数据 | 1500 - 20(IP) - 20(TCP) = **1460B** |
> [!QUESTION] 为什么下载大文件时网络层会有大量小包?
> 当你要发一个 100KB 的文件时,TCP 会按 MSS(1460B)将其切割成约 70 个段,每个段再交给网络层封装成 Packet,最后由链路层加上帧头帧尾变成 Frame。**分层的每一级都有自己的大小限制**,超出就要分片或分段——这就是 Path MTU Discovery(路径 MTU 发现)存在的原因:找到整条路径上最小的 MTU,避免中间路由器做 IP 分片(分片会严重影响性能)。
## 分层在代码中的体现
@@ -40,19 +40,19 @@ flowchart LR
```mermaid
sequenceDiagram
participant App as HTTP 应用
participant TCP as TCP Socket
participant IP as 网络层 IP
participant ETH as 以太网驱动
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() 返回 → 帧已发出 ✅
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 帧完整结构
@@ -69,22 +69,26 @@ Offset Size Field 说明
总长度范围:**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 包的最大体积。超过会被分片(Fragmentation),但分片会降低性能且增加丢包风险——这也是现代应用层(如 TLS、gRPC)倾向于小包传输的原因。
> 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 数据
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..." 🎉
ETH->>IP: "FCS 校验通过, EtherType=0x0800, 剥离以太网头"
IP->>TCP: "IP Checksum 正确, Protocol=6 TCP, 剥离 IP 头"
TCP->>App: "按 Sequence Number 重组, 剥离 TCP 头"
App->>App: "解析 GET / HTTP/1.1..."
```
每层的动作可以概括为三步:
@@ -95,32 +99,88 @@ sequenceDiagram
## 各层 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 | 物理层信号 | 电信号 / 光脉冲 |
| 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 路由器修改了中间层的地址:
```
发送端内部主机 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 │
└──────────────────────┘ └──────────────────────┘ └──────────────────────┘
```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 端口。这也意味着 **传输层的 Checksum 需要重新计算**。
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/OSI与TCP-IP模型对比]] — 分层模型的起源与对比
- [[hhs/NETWORK/IPv4协议详解]] — IP 包的详细格式
- [[hhs/NETWORK/TCP状态机详解]] — TCP 段的结构与状态管理
- [[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 的详细原理与配置
@@ -48,7 +48,7 @@ sequenceDiagram
Src->>Bcast: ARP Request "192.168.1.20 是谁?→ ff:ff:ff:ff:ff:ff"
Dst-->>Src: ARP Reply "我是!MAC = aa:bb:cc:11:22:33"
Src->>Cache: 写入缓存条目 (TTL ≈ 15~30 分钟)
Src->>Cache: 写入缓存条目 (Linux 默认 ≈ 30s, Windows ≈ 15~45s, 可配置)
Note over Src,Dst: 之后的通信直接用此 MAC,不再发 ARP
```
@@ -67,7 +67,7 @@ $ arp -a
## 二、IP 地址(IPv4 32 位 / IPv6 128 位)
### IPv4 地址结构
### IPv4 首部结构
```
版本(4 bits) IHL(4 bits) DSCP(8 bits) Total Length(16 bits)
@@ -91,6 +91,31 @@ Options (可选)...
| NAT | 广泛使用(缓解地址耗尽) | 不需要(地址充足) |
| SLAAC | — | 支持无状态自动配置 |
| 组播 | 有限支持 | 原生支持 |
| 地址解析 | ARP(广播) | NDP / Neighbor Discovery(组播,替代 ARP) |
### IPv6 地址表示与压缩
IPv6 地址共 128 位,用冒号分隔为 8 组,每组 4 个十六进制数:
```
完整写法: 2001:0db8:0000:0000:0000:0000:0000:0001
压缩规则 1 — 每组前导零可省略: 2001:db8:0:0:0:0:0:1
压缩规则 2 — 连续全零组用 :: 替代(仅一次): 2001:db8::1
```
> [!QUESTION] `::` 能用几次?
> 只能用**一次**。如果出现两个 `::`,解析器无法确定每组省略了多少个零,地址就会歧义。
### IPv6 特殊地址
| 地址 | 用途 |
|------|------|
| `::1` | 环回地址,等价于 IPv4 的 `127.0.0.1` |
| `::` | 未指定地址,等价于 IPv4 的 `0.0.0.0` |
| `fe80::/10` | 链路本地地址(Link-Local),同一链路自动通信 |
| `ff00::/8` | 组播地址 |
> 更多 IPv6 细节见 [[hhs/NETWORK/03-网络层/03-IPv6地址与扩展头部]]。
### IP 地址分类(历史)
@@ -104,6 +129,29 @@ Options (可选)...
> [!warning] CIDR 已淘汰分类编址
> 现代网络全部使用 **CIDR(Classless Inter-Domain Routing)** 无类编址,子网掩码可以是任意位数(如 /23、/27)。
> CIDR 用"前缀长度"取代了固定的 A/B/C 类划分,例如 `192.168.1.0/24` 表示前 24 位是网络号,剩余 8 位是主机号。
> 更多细节见 [[hhs/NETWORK/03-网络层/02-CIDR与子网划分]]。
### 特殊 IP 地址
| 地址 | 用途 |
|------|------|
| `127.0.0.1` / `::1` | 环回地址(Loopback),本机内部通信 |
| `0.0.0.0` | 表示"本机所有网卡"(服务端 bind 时常用) |
| `169.254.x.x` | 链路本地地址(DHCP 失败时自动分配) |
### 私有 IP 地址段
互联网上的路由器**不会转发**来自以下范围的数据包,它们仅用于内网,通过 NAT 转换为公网 IP 后才能访问互联网:
| 类别 | 地址范围 | CIDR | 可用地址数 |
|------|---------|------|-----------|
| A 类 | `10.0.0.0` ~ `10.255.255.255` | `10.0.0.0/8` | ~1677 万 |
| B 类 | `172.16.0.0` ~ `172.31.255.255` | `172.16.0.0/12` | ~104 万 |
| C 类 | `192.168.0.0` ~ `192.168.255.255` | `192.168.0.0/16` | ~6.5 万 |
> [!tip] 私有地址 + NAT = 解决 IPv4 耗尽
> 一个公网 IP 可以通过 NAT 映射背后成百上千个私有 IP,这就是为什么 IPv4 至今仍在大规模使用。详见 [[hhs/NETWORK/03-网络层/08-NAT原理与应用]]。
## 三、端口号(16 位)
@@ -134,7 +182,8 @@ flowchart LR
| 端口 | 协议 | 服务 |
|------|------|------|
| 21 | FTP | 文件传输控制 |
| 20 | FTP-Data | 文件传输数据通道 |
| 21 | FTP | 文件传输控制通道 |
| 22 | SSH | 安全远程登录 |
| 25 | SMTP | 邮件发送 |
| 53 | DNS | 域名解析 |
@@ -164,6 +213,7 @@ flowchart TD
## 关联笔记
- [[hhs/NETWORK/OSI与TCP-IP模型对比]] — 三层地址对应 OSI 的不同层级
- [[hhs/NETWORK/IPv4协议详解]] — IP 地址的详细编码与 CIDR 计算
- [[hhs/NETWORK/TCP状态机详解]] — 四元组如何唯一标识 TCP 连接
- [[hhs/NETWORK/01-基础概念/01-OSI与TCP-IP模型对比]] — 三层地址对应 OSI 的不同层级
- [[hhs/NETWORK/03-网络层/01-IPv4首部与分段重组]] — IP 首部的详细字段与分段机制
- [[hhs/NETWORK/03-网络层/02-CIDR与子网划分]] — CIDR 计算与子网划分实战
- [[hhs/NETWORK/04-传输层/01-TCP段结构与状态机]] — 四元组如何唯一标识 TCP 连接
@@ -1,5 +1,5 @@
---
tags: [计算机网络, 带宽, 延迟, RTT, 吞吐量, BDP]
tags: [计算机网络, 带宽, 延迟, RTT, 吞吐量, BDP, Jitter, Goodput]
create time: 2026-05-17 22:40
---
@@ -29,26 +29,52 @@ create time: 2026-05-17 22:40
> [!tip] bit vs Byte:记住除 8
> 运营商说的 "100M 宽带" 是 **100 Mbps**。换算成下载速度约 `100 ÷ 8 ≈ 12.5 MB/s`。
### 延迟(Latency / Propagation Delay)
### 延迟(Latency)
信号从发送端传播到接收端所需的时间,仅取决于**物理距离和介质光速**:
一个数据包从发送到被接收所经历的**总时间**,由四部分组成:
$$\text{Prop. Delay} = \frac{\text{Distance}}{v}$$
$$\text{Latency} = \text{传播延迟} + \text{传输延迟} + \text{排队延迟} + \text{处理延迟}$$
其中 $v$ 是传播速度。光纤中光速约为 $2 \times 10^8$ m/s(真空中光速的 2/3)。
```mermaid
flowchart LR
A["传播延迟<br/>Propagation"] --> B["传输延迟<br/>Transmission"]
B --> C["排队延迟<br/>Queuing"]
C --> D["处理延迟<br/>Processing"]
| 场景 | 典型延迟 |
style A fill:#DDA0DD,color:#000
style B fill:#87CEEB,color:#000
style C fill:#FFD700,color:#000
style D fill:#98FB98,color:#000
```
| 延迟类型 | 公式 | 取决于 | 典型值 |
|---------|------|--------|--------|
| **传播延迟** | $\frac{\text{Distance}}{v}$ | 物理距离、介质 | 光纤中 $v \approx 2 \times 10^8$ m/s(真空中光速的 2/3) |
| **传输延迟** | $\frac{\text{Packet Size}}{\text{Bandwidth}}$ | 数据包大小、链路带宽 | 1500B 以太网帧在 1Gbps 链路上 ≈ 12 μs |
| **排队延迟** | 不确定 | 路由器拥塞程度 | 空闲时 ≈ 0,拥塞时可达数百 ms |
| **处理延迟** | 不确定 | 路由器/交换机性能 | 通常 < 1 ms |
> [!question] 这四类延迟,哪个最"不可控"?
> 排队延迟。传播延迟由物理距离决定(选不了机房位置就改不了),传输延迟由包大小和带宽决定,处理延迟通常很小。但排队延迟随网络拥塞程度剧烈波动——这也是为什么它直接关联**抖动(Jitter)** 和 **丢包**。
| 场景 | 典型单向延迟(主要贡献:传播延迟) |
|------|---------|
| 同机房内 | 0.1–1 ms |
| 同城(同一城市数据中心) | 1–5 ms |
| 跨省(国内) | 20–40 ms |
| 跨洋(中美) | 80–150 ms |
| 卫星轨道(LEO) | 20–40 ms |
| TCP 握手 + TLS 1.3 | 1~2 RTT(额外 RTT × 加密计算时间) |
> [!tip] TCP + TLS 的额外延迟
> 建立一个 HTTPS 连接还需要 **TCP 三次握手(1 RTT)+ TLS 1.3 握手(1 RTT)**,在数据发出前就已经"花掉"了 2 个 RTT。这也是 HTTP/2、连接复用和 TLS Session Resumption 存在的重要原因。
### RTT(Round-Trip Time)
从发送方发出数据包到收到确认应答的总时间。**RTT = 2 × 单向延迟 + 排队处理时间 + 传输时间**。
从发送方发出数据包到收到确认应答的总时间。严格来说:
$$\text{RTT} = 2 \times (\text{传播延迟} + \text{传输延迟} + \text{排队延迟} + \text{处理延迟})$$
实际测量中,`ping` 工具测得的 RTT 主要反映传播延迟(往返),而 TCP 的 RTT 估算(用于计算 RTO)还需包含协议栈处理时间。
```mermaid
flowchart LR
@@ -60,6 +86,22 @@ flowchart LR
style D fill:#98FB98,color:#000
```
### 抖动(Jitter)
连续数据包之间延迟的**变化量**。即使平均延迟很低,抖动大也会严重影响体验:
$$\text{Jitter} = |\text{RTT}_n - \text{RTT}_{n-1}|$$
| 应用类型 | 对抖动的敏感度 | 说明 |
|---------|--------------|------|
| 语音通话 / 视频会议 | 🔴 极高 | 抖动 > 30ms 就会出现卡顿、断续 |
| 在线游戏 | 🔴 高 | 操作响应不稳定,玩家体验崩塌 |
| 文件下载 | 🟢 无所谓 | 只关心总吞吐量,不在乎每个包的到达节奏 |
| HTTP API 调用 | 🟡 中等 | 长尾延迟影响 P99 响应时间 |
> [!question] 如何对抗抖动?
> 答案是**接收端缓冲(Jitter Buffer)**。音视频应用会在接收端先攒一小段数据再播放,用少量额外延迟换取平滑输出。WebRTC 的 jitter buffer 通常 20–200ms。
### 吞吐量(Throughput)
单位时间内**实际成功传输的数据量**,受限于最窄环节:
@@ -86,6 +128,11 @@ flowchart LR
> [!question] 为什么我的千兆网卡只跑到 100MB/s?
> 因为千兆以太网理论上限是 `1Gbps ÷ 8 = 125MB/s`,去掉以太网帧头、IP 头、TCP 头等协议开销后,纯 Payload 大约 **94 MB/s**。如果还用了 HTTPS/TLS,CPU 加解密还会进一步限制吞吐量。
> [!info] Goodput vs Throughput:别搞混
> **Throughput**(吞吐量)是单位时间内传输的**总数据量**(含协议头)。**Goodput**(有效吞吐量)是应用层实际收到的**有效数据量**,要扣除所有协议头、重传包、确认包等开销。
> $$\text{Goodput} < \text{Throughput} \leq \text{Bandwidth}$$
> 用 `iperf3` 测出来的通常是 throughput;你的文件下载速度反映的更接近 goodput。
## 带宽时延积(BDP)
**BDP(Bandwidth-Delay Product)** 定义了链路上"在途"数据的最大量——这是维持满带宽所需的发送缓冲大小:
@@ -138,28 +185,46 @@ flowchart TD
## Go 中的实践示例
```go
// 设置 TCP 连接保持活跃,定期检测死链接
tcp := net.ListenConfig{
Control: func(network, address string, c syscall.RawConn) error {
return c.Control(func() {
// 开启 keepalive,每 30s 探测一次
syscall.SetsockoptInt(int(c.Fd()), syscall.SOL_SOCKET,
syscall.SO_KEEPALIVE, 1)
})
},
}
### 测量 RTT(TCP 连接延迟)
// HTTP Transport 的连接池配置
transport := &http.Transport{
MaxIdleConns: 100, // 最多空闲连接数
MaxIdleConnsPerHost: 10, // 每个 host 最多 10 个空闲
IdleConnTimeout: 90 * time.Second, // 空闲超时释放
```go
// 测量一次 TCP 连接的 RTT —— 最直接的延迟指标
func measureRTT(addr string) (time.Duration, error) {
start := time.Now()
conn, err := net.DialTimeout("tcp", addr, 5*time.Second)
if err != nil {
return 0, err
}
conn.Close()
return time.Since(start), nil // TCP 握手完成 ≈ 1 RTT
}
```
### 按 BDP 调大 TCP 缓冲区
```go
// 高带宽长延迟链路(如跨洋 100Mbps, RTT 150ms)的 BDP = 1.875MB
// 系统默认的 TCP 窗口(128KB~256KB)远远不够
transport := &http.Transport{
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
d := net.Dialer{}
conn, err := d.DialContext(ctx, network, addr)
if err != nil {
return nil, err
}
tcpConn := conn.(*net.TCPConn)
// 根据 BDP 设置读写缓冲区(2MB,略大于 BDP)
tcpConn.SetReadBuffer(2 << 20)
tcpConn.SetWriteBuffer(2 << 20)
return tcpConn, nil
},
}
```
> [!tip] 为什么不直接用系统默认窗口?
> Linux 默认 `tcp_rmem` 的最大值通常是 6MB,但单连接的初始窗口只有 ~128KB。对于高 BDP 链路,窗口增长(慢启动)到填满管道需要好几个 RTT,前几秒吞吐量会远低于带宽上限。应用层手动设大缓冲区 + 启用窗口缩放(`tcp_window_scaling`)可以加速这个过程。
## 关联笔记
- [[hhs/NETWORK/OSI与TCP-IP模型对比]] — 网络分层中各层的角色
- [[hhs/NETWORK/TCP状态机详解]] — TCP 的拥塞控制直接影响吞吐量
- [[hhs/NETWORK/TCP内核参数调优]] — BDP 对应内核参数 rmem/wmem
- [[hhs/NETWORK/01-基础概念/01-OSI与TCP-IP模型对比]] — 网络分层中各层的角色
- [[hhs/NETWORK/09-性能优化/01-TCP内核调优与连接复用]] — BDP 对应内核参数 rmem/wmem
+120 -25
View File
@@ -7,7 +7,29 @@ create time: 2026-05-17 22:50
## 概述
Ethernet(以太网)是目前最主流的局域网技术,由 Xerox、DEC 和 Intel 在 1980 年联合制定(IEEE 802.3)。理解 Ethernet 帧结构是掌握网络底层通信的基础——每一帧都包含了足够的信息来让网卡判断"这是给我的吗?内容完整吗?上层协议是什么?"
Ethernet(以太网)是目前最主流的局域网技术。最初由 Robert Metcalfe 在 Xerox PARC 于 1973 年发明,1980 年由 Xerox、DEC、Intel 联合发布 DIX 标准,1983 年 IEEE 正式发布 802.3 标准。
理解 Ethernet 帧结构是掌握网络底层通信的基础——每一帧都包含了足够的信息来让网卡判断"这是给我的吗?内容完整吗?上层协议是什么?"
## 物理层封装:前导码与帧间隔
在以太网帧到达网线之前,物理层(PHY)会自动加上 **前导码 (Preamble)** 和 **帧首定界符 (SFD)**,这部分对软件不可见,但理解它有助于理解"帧"与"包"的边界。
```
|<---------- Preamble + SFD (8B) -------->|<------------- Ethernet Frame ------------>|
┌───────────────────────┬────────┬─────────┬──────────┬──────────┬──────────┬─────────┐
│ Preamble (7 bytes) │ SFD(1B)│Dest MAC │ Src MAC │ EtherType│ Payload │ FCS │
│ 10101010 × 7 │10101011│ 6 bytes │ 6 bytes │ 2 bytes │ 46-1500B │ 4 bytes │
└───────────────────────┴────────┴─────────┴──────────┴──────────┴──────────┴─────────┘
↑ 14B Header
```
- **Preamble**: 7 字节的 `10101010` 交替模式,用于接收方的**时钟同步**(位同步)
- **SFD** (Start Frame Delimiter): `10101011`,最后两个连续 1 表示"帧数据从下一个字节开始"
- **IFG** (Inter-Frame Gap): 帧与帧之间至少间隔 **12 字节**(96 bit time),让网卡有时间处理上一帧
> [!question] 为什么前导码是 7 字节而不是 8 字节?
> 前 7 字节用于时钟同步,SFD 单独标记帧起始。接收方需要足够的跳变沿来锁定频率,7 字节 × 8 bit = 56 次跳变,在早期的 10Mbps 以太网中大约需要 5.6μs 完成同步。
## Ethernet II 帧格式(最常见)
@@ -39,34 +61,60 @@ Ethernet(以太网)是目前最主流的局域网技术,由 Xerox、DEC
现代 Gigabit+ 网卡已不用 CSMA/CD,但为了兼容仍保留此限制。
## IEEE 802.3 / 802.3Q(VLAN Tagged)帧
## IEEE 802.1Q(VLAN Tagged)帧
当网络中使用 VLAN 时,帧中会插入一个 4 字节的 802.1Q Tag:
当网络中使用 VLAN 时,帧中会**插入**一个 4 字节的 802.1Q Tag。注意:TPID 占据了原本 EtherType 的位置,真正的 EtherType 跟在 TCI 之后:
```
┌──────────┬──────────┬──────┬──────┬──────────┬─────────────┬──────────┐
│ Dest MAC │ Src MAC │ Type │ TPID │ TCI/VID │ Payload │ FCS │
│ 6 bytes │ 6 bytes │ 2B │2B │ 2B │ 46–1500 B │ 4 bytes │
└──────────┴──────────┴──────┴──────┴──────────┴─────────────┴──────────┘
┌──────────┬──────────┬─────────────┬──────────┬──────────┬─────────────┬──────────┐
│ Dest MAC │ Src MAC │ TPID (0x8100)│ TCI │ EtherType│ Payload │ FCS │
│ 6 bytes │ 6 bytes │ 2 bytes │ 2 bytes │ 2 bytes │ 42–1500 B │ 4 bytes │
│ │ │ │ PCP DEI │ 0x0800 │ │ │
│ │ │ │ 3b 1b │ │ │ │
│ │ │ │ VID(12b) │ │ │ │
└──────────┴──────────┴─────────────┴──────────┴──────────┴─────────────┴──────────┘
←──────── 4B 802.1Q Tag ────────→
```
新增字段:
- **TPID** (Tag Protocol Identifier): `0x8100` — 标识这是带 VLAN Tag 的帧
- **TCI** (Tag Control Information): 包含 PCP(优先级,3 bit)、CFI(规范格式指示符,1 bit)、**VID**(VLAN ID,12 bit)
- **TPID** (Tag Protocol Identifier): `0x8100` — 占据 EtherType 的位置,标识"这不是普通协议类型,而是 VLAN Tag"
- **TCI** (Tag Control Information): 包含 PCP(优先级,3 bit)、DEI(丢弃适格指示符,1 bit)、**VID**(VLAN ID,12 bit)
VID 取值范围 1–4094,共 4094 个可用 VLAN。
> [!note] CFI → DEI
> 早期标准中该位叫 CFI (Canonical Format Indicator),用于标记 MAC 地址是否为规范格式。802.1Q-2011 修订后改为 DEI (Drop Eligible Indicator),用于标识在网络拥塞时可优先丢弃的帧。
VID 取值范围 0–4095,其中 **VID 0** 用于表示优先级帧(不含 VLAN 信息),**VID 4095** 为保留值,实际可用 VLAN 为 1–4094(共 4094 个)。
> [!question] 插入 4 字节 Tag 后帧会不会超过最大长度?
> 会。带 Tag 的帧最大长度从 1518 字节扩展到 **1522 字节**。这也是为什么交换机端口通常区分"最大帧长度"配置。
## 常见 EtherType 对照表
| EtherType | 十六进制 | 协议 |
|-----------|---------|------|
| 协议 | EtherType | 说明 |
|------|-----------|------|
| IPv4 | `0x0800` | IP 数据包 |
| IPv6 | `0x86DD` | IPv6 数据包 |
| ARP | `0x0806` | 地址解析协议 |
| RARP | `0x8035` | 反向 ARP(历史遗留) |
| PPPoE | `0x8864` | PPP over Ethernet |
| PPPoE Discovery | `0x8863` | PPPoE 发现阶段 |
| PPPoE Session | `0x8864` | PPPoE 会话阶段 |
| 802.1Q | `0x8100` | VLAN 标记 |
| LACP | `0x8809` | 链路聚合控制协议 |
| LLDP | `0x88CC` | 链路层发现协议 |
### EtherType vs Length:如何区分?
这是以太网中一个经典的历史遗留问题。Ethernet II 和 IEEE 802.3 原始标准在同一个 2 字节位置上使用了**不同的语义**:
| 值范围 | 含义 | 对应标准 |
|--------|------|----------|
| ≥ `0x0600` (1536) | **EtherType** — 上层协议标识 | Ethernet II |
| ≤ `0x05DC` (1500) | **Length** — Payload 字节数 | IEEE 802.3 LLC/SNAP |
由于合法的 Payload 长度最大为 1500 字节,而合法的 EtherType 最小为 1536,两者不会重叠。**现代网络中绝大多数帧都是 Ethernet II 格式**,802.3 LLC/SNAP 仅在一些特殊场景(如 STP、某些工业协议)中出现。
> [!question] 如果你用 Wireshark 抓到一个该字段值为 0x05EE 的帧,它是什么?
> `0x05EE` = 1518,落在 1501–1535 之间——这是一个非法值,正常的以太网实现不会产生这样的帧。
## MAC 地址空间与 OUI
@@ -83,22 +131,69 @@ link/ether aa:bb:cc:dd:ee:ff brd ff:ff:ff:ff:ff:ff
# Linux 也可以用 ethtool -P eth0 查看出厂 MAC
```
### 单播/多播位(LSB of First Byte)
### 第一个字节的两个重要比特位
第一个字节的最低比特位决定寻址类型:
MAC 地址的第一个字节中,**最低两位**各自承载独立含义(注意:在网络传输中 MAC 地址按 **LSB first** 顺序发送,但日常书写采用 MSB first):
| LSB | 类型 | 示例 |
|-----|------|------|
| 0 | 单播 (Unicast) | `aa:bb:cc:dd:ee:ff` |
| 1 | 多播 (Multicast) | `01:00:5e:xx:xx:xx`(IPv4 组播) |
| 1 | 广播 (Broadcast) | `ff:ff:ff:ff:ff:ff` |
```
第一字节: b7 b6 b5 b4 b3 b2 b1 b0
↑ I/G 位 (Individual/Group)
↑ U/L 位 (Universal/Local)
```
#### I/G bit(bit 0)— 单播/组播
| 值 | 类型 | 说明 | 示例 |
|----|------|------|------|
| 0 | 单播 (Unicast) | 发给单一设备 | `aa:bb:cc:dd:ee:ff` |
| 1 | 组播 (Multicast) | 发给一组设备 | `01:00:5e:xx:xx:xx`(IPv4 组播) |
广播地址 `ff:ff:ff:ff:ff:ff` 是组播的特例(所有 bit 为 1)。
#### U/L bit(bit 1)— 全局/本地管理
| 值 | 类型 | 说明 |
|----|------|------|
| 0 | 全局管理 (GUA) | 由 IEEE 分配 OUI,厂商保证唯一(网卡出厂 MAC) |
| 1 | 本地管理 (LAA) | 管理员手动设置或软件生成(如 Docker 容器 MAC、虚拟网卡) |
> [!tip] 如何快速判断?看 MAC 第一个字节的十六进制最后一位
> - `a` → 1010 → LSB=0 → 单播
> - `3` → 0011 → LSB=1 → 多播/广播
> - `a` → 101**0** → I/G=0, U/L=0 → 单播 + 全局管理
> - `3` → 001**1** → I/G=1, U/L=1 → 组播 + 本地管理
> - `2` → 001**0** → I/G=0, U/L=1 → 单播 + 本地管理(如 Docker 生成的 MAC)
### MAC 地址的比特序
MAC 地址在网线上传输时采用 **LSB first**(小端序),这与我们日常书写 `aa:bb:cc:dd:ee:ff` 的 MSB first 习惯相反。这也解释了为什么 IPv4 组播 MAC 的前缀是 `01:00:5e` 而不是 `01:00:5f`——因为 `01` 的实际传输顺序是 `10000000`,I/G 位确实是第 1 个被发出的比特。
## MTU、MSS 与 Jumbo Frame
### 关键概念区分
| 概念 | 层级 | 标准值 | 说明 |
|------|------|--------|------|
| **MTU** (Maximum Transmission Unit) | 链路层 | 1500 bytes | 帧中 Payload 的最大长度(不含 Header 和 FCS) |
| **MSS** (Maximum Segment Size) | 传输层 (TCP) | 1460 bytes | TCP 数据段的最大载荷,= MTU - 20(IP) - 20(TCP) |
| **Max Frame** | 链路层 | 1518 bytes | 整个以太网帧(含 Header 14B + FCS 4B) |
### Jumbo Frame(巨型帧)
部分交换机和网卡支持 **Jumbo Frame**,将 MTU 提升到 **9000 字节**(甚至更大):
| 帧类型 | MTU | 最大帧长 | 适用场景 |
|--------|-----|----------|----------|
| 标准以太网 | 1500 B | 1518 B | 通用网络 |
| Jumbo Frame | 9000 B | 9018 B | 数据中心、iSCSI、NFS 存储网络 |
> [!warning] Jumbo Frame 的使用陷阱
> 1. **全链路必须一致**:路径上任何一台设备不支持 Jumbo Frame,就会导致分片或丢包
> 2. **非标准协议**:IEEE 从未正式标准化 Jumbo Frame(>1500 的 MTU),各家厂商实现略有差异
> 3. **实际收益**:减少帧头开销比例,CPU 中断次数降低,在大数据传输场景下吞吐量提升 10%–30%
## 关联笔记
- [[hhs/NETWORK/OSI与TCP-IP模型对比]] — 以太网属于链路层
- [[hhs/NETWORK/MAC地址与广播域]] — MAC 地址的工作范围
- [[hhs/NETWORK/Switch与路由器]] — Switch 如何使用 MAC 表转发帧
- [[hhs/NETWORK/01-基础概念/01-OSI与TCP-IP模型对比]] — 以太网属于链路层
- [[hhs/NETWORK/02-链路层/03-MAC地址与广播域]] — MAC 地址的工作范围
- [[hhs/NETWORK/02-链路层/04-Switch与路由器]] — Switch 如何使用 MAC 表转发帧
- [[hhs/NETWORK/02-链路层/02-CSMA-CD与以太网退避]] — CSMA/CD 碰撞检测机制
- [[hhs/NETWORK/02-链路层/05-VLAN与Trunk]] — 802.1Q VLAN 的实际部署
@@ -1,5 +1,5 @@
---
tags: [计算机网络, CSMA-CD, 以太网退避, 冲突检测]
tags: [计算机网络, CSMA-CD, 以太网退避, 冲突检测, 碰撞域, 全双工, 半双工]
create time: 2026-05-17 23:00
---
@@ -40,19 +40,39 @@ flowchart TD
| 3 | **Collision Detection** | 发送过程中持续监测是否有冲突信号 |
| 4 | **Collision Handling** | 检测到冲突 → 发 Jam Signal → 执行退避 → 重试 |
## Slot Time(时隙)
在讲解退避算法之前,先认识一个核心概念——**Slot Time(时隙/争用期)**:
$$\text{Slot Time} = 512 \text{ bit-time} = 51.2 \mu s \quad (\text{在 10Mbps 以太网下})$$
时隙是 CSMA/CD 中所有时间计算的基本单位,它的值约等于**最大往返传播延迟(~46.4μs)+ Jam Signal 时间(~3.2μs)+ 安全余量**。退避算法中的等待时间、最小帧长、冲突检测窗口,全部以它为基准。
> [!QUESTION] 为什么 Slot Time 要比理论往返延迟长一点?
> 因为检测到冲突后还需要发送 Jam Signal 确保所有节点都感知到碰撞。如果时隙刚好等于往返延迟,发送方在时隙结束时还没完成 Jam Signal 的发送,就可能误以为"没冲突"。
## Jam Signal(强化冲突信号)
检测到冲突后,发送方会立刻发出一个 **32 bit 的 Jam Signal**,目的有二:
1. **强化冲突**:确保网络上所有节点(包括距冲突点较远的节点)都能检测到碰撞
2. **延长冲突持续时间**:使冲突信号维持足够长的时间,避免有节点"刚好错过"碰撞窗口
Jam Signal 的传输时间约 3.2μs(10Mbps),已包含在 Slot Time 的计算中。
## 二进制指数退避算法
冲突后的等待时间通过随机退避来避免再次冲突:
冲突后的等待时间通过**随机退避**来避免再次碰撞——选到相同随机数的概率随窗口增大而降低:
$$\text{等待时间} = \min(r, 2^{10}-1) \times 512 \text{ bit-time}$$
$$\text{backoff} = r \times \text{Slot Time},\quad r \in \{0, 1, \ldots, \min(2^k - 1, 1023)\}$$
其中 $r$ 为重传次数,`512 bit-time` 是最小帧长对应的传输时间(即争用期)。
其中 $k$ 为该帧的重传次数。每次冲突后窗口翻倍(指数增长),上限固定在 1023。
```mermaid
flowchart TD
k["重传次数 k"] --> r["r = random(0 ~ 2^k - 1)"]
r --> capped["capped at 2^10 - 1 = 1023 (k > 10)"]
capped --> backoff["backoff_time = r × 512 bit-time"]
k["重传次数 k"] --> window["窗口大小 min(2^k - 1, 1023)"]
window --> r["r = random(0 ~ 窗口大小)"]
r --> backoff["backoff = r × 51.2μs"]
style k fill:#DDA0DD,color:#000
style backoff fill:#98FB98,color:#000
@@ -60,69 +80,140 @@ flowchart TD
### 退避表
| 重传次数 k | 窗口大小 2^k - 1 | 可能等待的 slot 数范围 |
|-----------|------------------|---------------------|
| 1 | 1 | {0, 1} |
| 2 | 3 | {0, 1, 2, 3} |
| 3 | 7 | {0..7} |
| 4 | 15 | {0..15} |
| 5–10 | 最大值 1023 | {0..1023} |
| >10 | 仍为 1023 | 上限固定 |
| 重传次数 k | 窗口大小 min(2^k - 1, 1023) | r 的取值范围 | 最大等待时间 |
|-----------|---------------------------|-------------|------------|
| 1 | 1 | {0, 1} | 51.2μs |
| 2 | 3 | {0..3} | 153.6μs |
| 3 | 7 | {0..7} | 358.4μs |
| 4 | 15 | {0..15} | 768μs |
| 5–10 | 2^k - 1 → 1023 | {0..1023} | 52.4ms |
| >10 | 1023(上限固定) | {0..1023} | 52.4ms |
如果重传 16 次仍然失败,帧被丢弃并向上层报告错误。
> [!tip] 为什么窗口要"封顶"在 1023?
> 如果不封顶,k=16 时窗口可达 65535,等待时间超过 3 秒——太久了。封顶保证了最坏情况下的重传延迟有上界,同时也意味着高冲突率下不同节点的退避选择可能重复,这就是网络严重拥塞时的"公平性代价"。
如果重传 **16 次**仍然失败,帧被丢弃并向上层报告错误。
### 示例
假设节点 A 和 B 同时发送,发生冲突:
```
时刻 0ms: A 和 B 同时发送 → 冲突!
两者都发送 32bit Jam Signal
时刻 0.1ms: A 选 r=3 → 等 3×512bit-time = 3×51.2μs ≈ 154μs
B 选 r=1 → 等 1×51.2μs ≈ 51μs
时刻 0.05ms: B 先等完,先尝试发送 → A 还在等
时刻 0.15ms: A 等完,发现信道已被 B 占用 → 退避重选 r
...
时刻 0μs: A 和 B 同时开始发送
时刻 23.2μs: A 检测到冲突(假设距冲突点约 23.2μs)
A 和 B 均发送 Jam Signal (32bit, 约 3.2μs)
此时 Slot Time 计时仍在继续...
时刻 51.2μs: Slot Time 结束,进入退避期
A 选 r=3 → 等 3 × 51.2μs = 153.6μs
B 选 r=1 → 等 1 × 51.2μs = 51.2μs
时刻 102.4μs: B 先等完 → 检测信道空闲 → 发送 ✅
时刻 204.8μs: A 等完 → 检测信道忙碌 → 等待...
(A 的本次重传计数 k=2,下次窗口扩大为 {0..3})
```
> [!QUESTION] 第二次冲突时,A 和 B 还是同时选到相同 r 怎么办?
> 第二次冲突时窗口翻倍到 {0..3},选到相同值的概率从 1/2 降到 1/4。指数退避的核心思想就是:**冲突越频繁,等待范围越大,再次碰撞的概率越低**。当然,极端情况下仍可能连续冲突——这就是为什么 16 次后要放弃。
## 最小帧长与争用期
### 为什么最小帧是 64 字节?
关键公式:**帧传输时间 ≥ 2 × 端到端传播延迟**
核心原则:**帧传输时间 ≥ 往返传播延迟**
如果帧太短,可能在冲突信号到达之前就已经发完了——发送方根本不知道发生过冲突。
如果帧太短,发送方可能在冲突信号返回之前就已经发完了——根本不知道发生过冲突,也就无法重传。
```mermaid
sequenceDiagram
participant A as 节点 A
participant H as 最远节点 H
Note over A,H: 传播延迟 = τ
participant A as "节点 A"
participant H as "最远节点 H"
Note over A,H: "传播延迟 τ"
A->>H: 开始发送一个超短帧 (长度 < 2τ)
H->>A: 冲突信号沿原路返回
Note over A: 此时 A 已经发完了...<br/>没检测到冲突!🔥
A->>H: "发送一个超短帧 (长度 < 2τ)"
H->>A: "冲突信号返回"
Note over A: "此时 A 已经发完了...<br/>没检测到冲突!"
```
所以规定:
所以必须满足:
$$\text{最小帧长} \geq 2 \times \tau \times \text{带宽}$$
$$\text{最小帧长} \geq 2 \times \tau_{\max} \times \text{带宽}$$
对于经典 10Mbps 以太网,最长电缆段 2500m,$\tau \approx 12.5\mu s$:
其中 $\tau_{\max}$ 是网络中最远两个节点之间的**单程传播延迟**。
$$\text{最小帧长} \geq 2 \times 12.5\mu s \times 10^7 \text{ bps} = 250,000 \text{ bits?}$$
### 从理论推导到工程标准
等等,实际工程中使用了中继器和更短的网段组合,最终标准定为 **512 bit-time = 64 bytes**。
对于经典 10Mbps 以太网(10BASE5 粗缆 + 4 个中继器,总跨度约 2500m):
## 现代以太网中的命运
$$\tau_{\max} \approx 46.4\mu s \quad \text{(含中继器延迟)}$$
| 年代 | 拓扑 | 介质 | CSMA/CD? |
|------|------|------|---------|
| 1980s | 总线型 | 同轴电缆 | ✅ 必需 |
| 1990s | 星型 + Hub | 双绞线 | ✅ 存在但极少冲突 |
| 2000s+ | 全双工 Switch | 双绞线/光纤 | ❌ 已禁用 |
代入公式:
全双工模式下,收发通道分离(两根线对),不存在碰撞的可能,CSMA/CD 自动关闭。从 Gigabit Ethernet (802.3ab) 起,交换机端口默认全双工运行。
$$\text{最小帧长} \geq 46.4\mu s \times 10^7 \text{ bps} \approx 464 \text{ bits}$$
IEEE 在此基础上统一向上取整,取了一个**干净的 2 的幂次方**——512 bits(64 bytes),这也是 Slot Time 的值。多出的 48 bits 作为安全余量,同时覆盖了 Jam Signal 的传输时间。
> [!tip] 帧长与 Slot Time 的关系
> 最小帧长 = Slot Time × 带宽 = 512 bit-time × 1 bps = **512 bits = 64 bytes**
>
> 这不是巧合——最小帧长的设计保证了:只要帧还在传输中(长度 ≥ Slot Time),冲突就一定能被检测到。退避算法以 Slot Time 为单位,最小帧长以 Slot Time 为下限,二者在设计上是统一的。
### 碰撞域(Collision Domain)
理解 CSMA/CD 就不得不理解**碰撞域**——所有共享同一冲突检测范围的节点组成一个碰撞域:
```mermaid
graph LR
subgraph CD1["碰撞域 1 (Hub 连接)"]
A["节点 A"] --- Hub["Hub 集线器"]
B["节点 B"] --- Hub
C["节点 C"] --- Hub
end
Switch["交换机"] --- Hub
Switch --- D["节点 D"]
Switch --- E["节点 E"]
subgraph CD2["碰撞域 2"]
D
E
end
style CD1 fill:#FFE4E1,stroke:#FF6B6B,stroke-width:2px
style CD2 fill:#E1F5FE,stroke:#4FC3F7,stroke-width:2px
```
- **Hub**:不隔离碰撞域——所有连接到同一 Hub 的设备属于**同一个**碰撞域
- **Switch**:每个端口是一个独立碰撞域,从根本上消除了 CSMA/CD 的需求
- **广播域**则不同,由 VLAN 划分,和 CSMA/CD 无关(详见 [[05-VLAN与Trunk]])
## 半双工 vs 全双工
CSMA/CD 的"监听-发送-检测-退避"机制本质上是**半双工**的——同一时间只能收或发,不能同时进行。全双工模式通过物理上分离收发通道彻底绕开了这个问题:
| 模式 | 通道 | 冲突 | CSMA/CD | 典型场景 |
|------|------|------|---------|---------|
| 半双工 (Hub) | 共享 | 存在 | 必需 | 早期总线/Hub 网络 |
| 全双工 (Switch) | 收发分离 | 不存在 | 自动关闭 | 现代交换网络 |
从 **Gigabit Ethernet (802.3ab)** 起,交换机端口默认全双工运行。虽然标准中仍保留了半双工 CSMA/CD 定义(用于兼容),但实际网络中已几乎不再使用。
```mermaid
graph LR
subgraph HalfDuplex["半双工 (Hub)"]
direction LR
H1["节点"] -- "发送" --> H2["Hub"]
H2 -- "接收" --> H1
end
subgraph FullDuplex["全双工 (Switch)"]
direction LR
S1["节点"] -- "发送" --> S2["Switch"]
S2 -- "接收" --> S1
end
style HalfDuplex fill:#FFF3E0,stroke:#FF9800
style FullDuplex fill:#E8F5E9,stroke:#4CAF50
```
> [!note] 半双工 vs 全双工的本质区别
> - **半双工**:收发共用一条通道,同一时刻只能单向通信,需要 CSMA/CD 协调
> - **全双工**:收发各有独立通道(物理上两对线),可同时双向通信,CSMA/CD 自动关闭
> [!note] 如何检查当前网卡是否启用了 CSMA/CD?
> ```bash
@@ -130,9 +221,11 @@ $$\text{最小帧长} \geq 2 \times 12.5\mu s \times 10^7 \text{ bps} = 250,000
> Supports Collision Detection: yes
> Current Collision Detection: disabled (full-duplex)
> ```
> 现代网卡通常显示 "disabled (full-duplex)",说明 CSMA/CD 已关闭。
## 关联笔记
- [[hhs/NETWORK/Ethernet帧结构]] — 以太网 II 帧的格式定义
- [[hhs/NETWORK/Switch与路由器]] — Switch 如何通过全双工消除冲突
- [[hhs/NETWORK/VLAN与Trunk]] — VLAN 隔离冲突域
- [[01-Ethernet帧结构]] — 以太网 II 帧的格式定义,64 字节最小帧长的来源
- [[04-Switch与路由器]] — Switch 如何通过全双工消除冲突
- [[05-VLAN与Trunk]] — VLAN 如何隔离广播域
- [[03-MAC地址与广播域]] — 广播域与碰撞域的区别