Files
cs-note/hhs/NETWORK/02-链路层/02-CSMA-CD与以太网退避.md
T
2026-05-27 00:12:00 +08:00

232 lines
9.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
tags: [计算机网络, CSMA-CD, 以太网退避, 冲突检测, 碰撞域, 全双工, 半双工]
create time: 2026-05-17 23:00
---
# CSMA/CD 与以太网退避
## 概述
CSMA/CD(Carrier Sense Multiple Access / Collision Detection,载波监听多路访问/冲突检测)是早期共享式以太网的 MAC 子层协议。虽然现代交换式以太网已不再使用它,但理解其原理对掌握网络演进和碰撞域概念至关重要。
> [!QUESTION] 为什么需要 CSMA/CD?
> 在共享介质(如同轴电缆或 Hub 集线器)上,多台设备共用同一根网线。如果没有协调机制,两台同时发送会导致信号叠加破坏——就像两个人同时在同一频道说话,谁也听不清。
## CSMA/CD 工作流程
```mermaid
flowchart TD
Start["有帧要发送"] --> Wait["等待信道空闲?"]
Wait -->|"空闲"| Send["开始发送"]
Wait -->|"忙碌"| Wait
Send --> Check{"是否在 512bit<br/>时间内检测到冲突?"}
Check -->|"否"| Done["发送完成 ✅"]
Check -->|"是"| Collide["发生冲突 → 发送 Jam Signal"]
Collide --> Backoff["指数退避算法计算等待时间"]
Backoff --> Retry{"重传次数 < 16?"}
Retry -->|"是"| Wait
Retry -->|"否"| Fail["丢弃帧,上报错误 ❌"]
style Done fill:#98FB98,color:#000
style Fail fill:#FF6B6B,color:#fff
```
### 四个步骤详解
| 步骤 | 英文名称 | 说明 |
|------|---------|------|
| 1 | **Carrier Sense** | 发送前先监听:信道忙就等,空闲才发 |
| 2 | **Multiple Access** | 多个设备共享同一介质 |
| 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{backoff} = r \times \text{Slot Time},\quad r \in \{0, 1, \ldots, \min(2^k - 1, 1023)\}$$
其中 $k$ 为该帧的重传次数。每次冲突后窗口翻倍(指数增长),上限固定在 1023。
```mermaid
flowchart TD
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
```
### 退避表
| 重传次数 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 |
> [!tip] 为什么窗口要"封顶"在 1023?
> 如果不封顶,k=16 时窗口可达 65535,等待时间超过 3 秒——太久了。封顶保证了最坏情况下的重传延迟有上界,同时也意味着高冲突率下不同节点的退避选择可能重复,这就是网络严重拥塞时的"公平性代价"。
如果重传 **16 次**仍然失败,帧被丢弃并向上层报告错误。
### 示例
假设节点 A 和 B 同时发送,发生冲突:
```
时刻 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 字节?
核心原则:**帧传输时间 ≥ 往返传播延迟**
如果帧太短,发送方可能在冲突信号返回之前就已经发完了——根本不知道发生过冲突,也就无法重传。
```mermaid
sequenceDiagram
participant A as "节点 A"
participant H as "最远节点 H"
Note over A,H: "传播延迟 τ"
A->>H: "发送一个超短帧 (长度 < 2τ)"
H->>A: "冲突信号返回"
Note over A: "此时 A 已经发完了...<br/>没检测到冲突!"
```
所以必须满足:
$$\text{最小帧长} \geq 2 \times \tau_{\max} \times \text{带宽}$$
其中 $\tau_{\max}$ 是网络中最远两个节点之间的**单程传播延迟**。
### 从理论推导到工程标准
对于经典 10Mbps 以太网(10BASE5 粗缆 + 4 个中继器,总跨度约 2500m):
$$\tau_{\max} \approx 46.4\mu s \quad \text{(含中继器延迟)}$$
代入公式:
$$\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
> $ ethtool eth0 | grep -i coll
> Supports Collision Detection: yes
> Current Collision Detection: disabled (full-duplex)
> ```
> 现代网卡通常显示 "disabled (full-duplex)",说明 CSMA/CD 已关闭。
## 关联笔记
- [[01-Ethernet帧结构]] — 以太网 II 帧的格式定义,64 字节最小帧长的来源
- [[04-Switch与路由器]] — Switch 如何通过全双工消除冲突
- [[05-VLAN与Trunk]] — VLAN 如何隔离广播域
- [[03-MAC地址与广播域]] — 广播域与碰撞域的区别