---
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
时间内检测到冲突?"}
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 已经发完了...
没检测到冲突!"
```
所以必须满足:
$$\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地址与广播域]] — 广播域与碰撞域的区别