--- 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地址与广播域]] — 广播域与碰撞域的区别