ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

从物理层到分组交换:以太网封装、CSMA/CD与冲突域排查

从物理层到分组交换:以太网封装、CSMA/CD与冲突域排查

简介:围绕网络基础物理层展开的这份 docx 文档,面向嵌入式开发、网络入门与 TCP/IP 基础学习者,重点梳理数据封装与解封装的完整路径,以及电路交换、报文交换、分组交换的差异与适用逻辑。内容从“最小网络三要素”切入,串联网线、网卡、协议栈与传输介质,讲解同轴电缆、双绞线、光纤等介质的特性,并以 10BASE5、共享型网络、冲突域和 CSMA/CD 为例说明以太网早期标准与冲突避免机制,帮助读者建立从比特流到上层数据的整体认知。资源包共 1 个 docx 文件,约 3.46MB,便于集中阅读和做笔记。已有 106 人学习,适合用作网络基础课程复习、嵌入式网络排错前的概念梳理,也可为理解报文标识、分片传输与链路公平性等问题提供较直观的参考。

1. 从一根同轴电缆说起:物理层为什么决定了上层协议的样子

学网络基础的人大多从 TCP/IP 分层图开始背,背完三次握手就以为懂了,结果一上设备就发现:真正让人翻车的往往不是协议栈那几层,而是网线、网卡、编码这些看起来最没技术含量的东西。物理层只干一件事——把 0 和 1 变成线上电平的高低、光纤里光的明暗,可正是这件小事,逼出了后面一整套机制:共享一根同轴电缆,才有了冲突域和 CSMA/CD;用双绞线跑千兆,才逼得八根线全用上;把报文切成固定长度,才让多台设备共用一条链路变得公平。

做嵌入式的人尤其躲不开这一层。设备小、协议栈基本集成在芯片或模组里,买来的网络相关硬件几乎都自带 tcpip 能力,出问题时往上查协议栈经常一无所获,往下看网卡、介质、协商参数反而一击命中。把 Ethernet 从比特流到封装这条链路拆开看一遍,这套东西才不再是黑匣子。

2. 三种交换方式:为什么把报文切成固定长度才算“公平”

2.1 电路交换:先建路,独占到底

电路交换的思路是“先占坑”。双方开始通信之前,网络先为这次通信维护一条逻辑电路,这条线路只传递你的数据,不会混进别人的数据。好处很直接:路径固定、时延稳定、包的顺序天然有保证,不需要在每个数据块上写收发地址,中间设备也不用做转发决策。早期的语音业务选它,就是因为语音是连续流,宁可浪费一点容量也要保证不抖动。

代价同样明显。线路一旦建立就被独占,哪怕某一时刻双方都不说话,这条逻辑电路仍然占着,别人无法使用它做数据传输。对突发性很强的数据业务来说,这种占用极其浪费——你传一个文件,绝大部分时间线路在等你准备下一段数据。所以电路交换的核心矛盾是:用资源预留换取确定性,而数据通信大多数时候并不需要这种确定性。

2.2 报文交换:加标识解决混乱,但没解决公平

当两台以上设备想同时使用同一条线路发送数据时,麻烦来了:数据在传输过程中会混在一起,接收方分不清哪一段是谁的。报文交换的办法是给每段数据加一个标识,相当于给每个包裹贴上收发地址,接收方按标识分拣,就不会把数据传乱。

这是“存储转发”的雏形:中间设备先把整个报文收下来,看清标识,再决定往哪发。它解决了多设备共用线路的混乱问题,但留了两个坑。第一个是缓冲压力——报文没有大小上限,中间设备必须能整包收下,一个超长报文就能把缓冲区吃满。第二个是不公平:报文大小不一致时,一个长报文会长时间霸占线路,后面排队的短报文只能干等,这种排队阻塞在流量一大时会被放大成明显的时延抖动。

2.3 分组交换:定长分片和报头字段的由来

分组交换保留了报文交换的标识思路,但多做了一件事:给每个数据段的大小定一个上限,先做分片,每片不超过这个值,再加一个标识,说明这份数据是从哪个设备到哪个设备的。这样一来,多台设备共用同一条链路时,每个包占用线路的时间有了天花板,长报文无法无限期霸占介质。

原文提到“数据大小不一致会出现不公平”,解决办法是规定一个固定值,每次只传输这么大的值。这个固定值就是后来 MTU、MSS 这类概念的源头。用一段简化代码把分片过程写出来,报头里到底要放什么就清楚了:

# 把上层报文切成不超过上限的分片,每片补上报头 MTU_PAYLOAD = 1024 # 单片可承载的净荷上限(字节),即“每次只传这么大的值” def fragment(payload: bytes, src: str, dst: str, flow_id: int): pieces = [] total = (len(payload) + MTU_PAYLOAD - 1) // MTU_PAYLOAD # 向上取整得分片数 for seq in range(total): chunk = payload[seq * MTU_PAYLOAD:(seq + 1) * MTU_PAYLOAD] header = { "src": src, # 源设备标识,接收方据此判断数据从哪来 "dst": dst, # 目的设备标识,中间设备据此转发 "flow": flow_id, # 同一条报文的所有分片共享流标识,防止串包 "seq": seq, # 片序号,接收方按序重组 "last": seq == total - 1, # 末片标记,省掉单独的长度字段 } pieces.append((header, chunk)) return pieces print(len(fragment(b"x" * 2500, "A", "B", 7))) # 3 片:1024 + 1024 + 452

MTU_PAYLOAD决定单个分片的净荷上限,值越小,公平性越好但报头开销占比越高;src/dst对应原文说的“标识数据从哪个设备到哪个设备”;flow是为了让不同报文的分片不互相污染,这一点原文没写,但任何实际分片实现都得有;seq和last是重组必需的信息。分片的代价是每个包都要背一份报头——以太网头 14 字节加 IP 头 20 字节,若净荷只有几十字节,开销比例能到一半,所以固定值不能定得太小。

把三种方式放在一起对比,取舍关系就很清楚了:

交换方式资源占用时延特性主要开销典型问题
电路交换建立后独占稳定、抖动小建链时间、空闲浪费突发业务利用率极低
报文交换按包占用排队时延不可预测中间设备大缓冲长报文阻塞短报文
分组交换按片占用较稳定、可流水每包报头开销乱序、需要重组

分组交换真正解决的是“公平”和“可流水”这两件事:前一个包还在路上,后一个包就可以从下一台设备发出,链路利用率被拉起来了。代价是接收端要做重组、要处理乱序、要应付丢片重传——上层协议里大量的复杂度,根子都在这。

3. 封装与解封装:一个包裹从应用到比特流要经过几层

3.1 快递模型:报头给中间设备看,载荷才是对方要的

封装这件事用快递打比方最贴切。快递包装盒里塞了很多东西——泡沫、填充物、说明书,最终对方真正要的只是里面那个小物件,这个小物件就像应用层的数据。盒子外面写的收件地址、电话,是给中间转运站看的;收件人拆开盒子,填充物直接扔掉,只留下小物件。网络里的封装完全一样:从上层到下层,每层在自己的数据外面套一层头,物理层最后把它变成 0101 发出去;接收方从下层到上层一层层解封装,每层剥掉自己认识的那层头,把剩下的载荷交给上一层。

关键在于“每层只关心自己那层头”。交换机看链路层头,路由器看网络层头,它们不需要理解应用层到底传的是文字还是图片。这种分层让中间设备可以做得足够简单、足够快。用一段嵌套结构把封装顺序写出来:

# 自顶向下封装:每层只在自己的载荷外面套一层头 app = {"layer": "app", "data": b"hello"} # 应用层真正要传的小物件 tcp = {"layer": "tcp", "sport": 50000, "dport": 8080, "payload": app} # 传输层加端口 ip = {"layer": "ip", "src": "10.0.0.2", "dst": "10.0.0.9", "payload": tcp} # 网络层加地址 eth = {"layer": "eth", "src_mac": "AA:AA:AA:AA:AA:01", "dst_mac": "BB:BB:BB:BB:BB:02", "payload": ip} # 链路层加 MAC frame = encode_to_bits(eth) # 物理层只认这一串 0101

每层的payload指向上一层的整包,而不是只指向应用数据;src_mac/dst_mac只在同一个广播域里有意义,出了这个范围会被下一跳设备改写;解封装就是逆过程,从比特流开始,逐层校验、逐层剥头。实际实现里还会在链路层尾部加 CRC,收方算一遍对不上就整帧丢弃,这也是为什么抓包时经常看到“帧校验错误”的计数。

3.2 最小网络三要素:网线、网卡、协议栈

网络诞生之前,跨机器交换数据靠的是刻录介质搬来搬去,人肉带宽。有了网络之后,要凑成一个能用的最小网络,三样东西缺一不可:网线、网卡、协议栈。

网线负责传比特流,是纯粹的物理通道;网卡负责把主机内部的并行数据转成串行比特流、做编码、驱动介质收发,顺便完成冲突检测和校验;协议栈负责封装解封装、寻址、重传这些逻辑。三者分工明确,出问题时也可以按这个顺序切分:链路不通先看介质和网卡,通了但通信异常再看协议栈。嵌入式设备上协议栈往往是集成好的,网卡和介质却是硬件设计、布线施工决定的,所以故障分布并不均匀——大部分“玄学”问题都落在后面这两样上。

3.3 物理层的最后一步:网卡怎么把 0101 变成线上电平

网卡拿到一帧数据后要做几件事:加前导码让收方对齐时钟、把字节流转成比特流、按物理层标准编码、驱动收发器把电平推到介质上。10BASE5 用的是曼彻斯特编码,规则很简单:每个比特位的中间必须发生一次跳变,1 表示高到低,0 表示低到高。用几行代码就能看清它干了什么:

def manchester(bits: str) -> str: # 1 -> 高到低跳变,0 -> 低到高跳变;每位中间必有跳变,接收方可从中恢复时钟 out = [] for b in bits: out.append("10" if b == "1" else "01") return "".join(out) print(manchester("1011")) # 输出 10100110,长度翻倍

每个比特变成两个电平符号,线路上的符号速率是数据速率的两倍——10Mbps 的数据流需要 20M 波特的信号,这是曼彻斯特编码用带宽换时钟的代价。这么设计的原因是收方不需要额外的时钟线:只要盯着每位中间的跳变,就能把自己的采样时刻对齐到发送方的节奏上。在只有一根同轴电缆、所有设备共享介质的年代,这个特性极其关键,因为收发双方没有其他同步手段。

代价也很实在。编码效率只有 50%,同样一段介质能承载的数据速率被砍半;另外前导码、帧间隙、冲突退避都在吃掉可用时间,标称 10M 的链路实际吞吐远达不到 10M。理解这一点,后面看到“为什么链路速率和实际传输速度对不上”就不会觉得奇怪了。

4. 共享介质与冲突域:10BASE5 的命名、双绞线的绞距、CSMA/CD 的四步

4.1 10BASE5 名字拆解:每个字符都在约束你

10BASE5 这个名字没有一个是随便写的。10 表示每秒可以传 10M 比特;BASE 表示基带传输,整条电缆的带宽直接给这一路数字信号用,不调制到高频载波上,所以一根线在同一时刻只能有一个信号在跑;5 表示单段传输距离 500 米,指的是粗同轴电缆的一个网段长度上限。

500 米这个数字不是拍脑袋定的,它同时受两个因素夹击。往下是信号衰减:同轴电缆对高频分量衰减明显,距离一长,收方就分辨不出电平高低。往上是冲突检测的时间窗口:CSMA/CD 要求发送方在帧还没发完的时候就能听到冲突,如果线太长,最远端的碰撞信号传回来时这帧已经发完了,发送方会误以为发送成功——常见的实现思路是用“往返时延必须小于最小帧发送时间”来约束,最小帧 64 字节、时隙 512 位时间这些约定,本质就是给距离和速率划一条不能越过的线。

同轴电缆要实现多设备互联,可以借助耦合器,每个接口下挂一个设备,画出来就是一根线上串了多个节点。这种结构叫共享型网络:所有设备共享同一根线,任何一台发的数据,理论上其他设备的网卡都能在电气上收到,只是靠 MAC 地址过滤决定要不要往上交。缺点两条:传输速度慢,没有私密性可言。

4.2 冲突域的物理成因与 CSMA/CD 的四步

冲突域指的是设备互相传输时会产生数据碰撞的那个范围。共享型网络里,整根同轴电缆就是一个冲突域,设备越多,冲突概率越高,冲突域越大,有效吞吐越低——这不是软件配置问题,是物理规律。因为同一时刻线上只能有一个信号,两个设备同时发,电平叠加后就变成谁都读不懂的噪声。

CSMA/CD 就是在这个物理限制下挤出来的效率。四步可以背成四句话:先听后发、忙则等待、边听边发、冲突停发。设备先听线上有没有电压摆动值,如果线是静止的,就开始发送;如果听到线被占用,就等。发送过程中还要继续听,因为不可避免会出现两台设备几乎同时听到“线是静止的”,然后一起发——这时候先听到线上有电压摆动的设备立刻停止发送,并发出冲突加强信号,让对方也尽快发现。停发之后进入退避:计算一个随机延迟,比如几微秒内不往线上发,时间到了再重新听、重新发。

原文里说“计算一个值,比如 10s”,这个数量级要纠正一下。真实退避是微秒级的,10M 以太网的一个时隙是 512 位时间,约 51.2 微秒。退避算法通常用二进制指数退避:

import random def backoff(attempt: int, slot: float = 51.2e-6) -> float: # 第 n 次冲突后,从 0 ~ 2^min(n,10)-1 里随机取 k,等 k 个时隙再重试 k = random.randint(0, 2 ** min(attempt, 10) - 1) return k * slot print(backoff(1)) # 第 1 次冲突:0 或 1 个时隙 print(backoff(5)) # 第 5 次冲突:0 ~ 31 个时隙,窗口显著变大 print(backoff(12)) # 超过 10 次后窗口封顶,避免等待时间无限膨胀

attempt是连续冲突次数,冲突越多,随机窗口越大,多个设备同时选中同一个等待值的概率就越低;slot是一个争用时隙,取 512 位时间是因为最小帧必须在冲突信号返回之前还在发送,这个值由速率和最大网段长度共同决定;封顶在 10 次是为了不让退避时间失控,超过一定次数就上报上层。这段逻辑的价值在于:用随机化把确定性的碰撞变成概率上可收敛的重试,网络负载轻时几乎感觉不到,负载重时吞吐下降但不会彻底死锁。

4.3 双绞线的绞距与 5 类/6 类:千兆为什么八根线全用

同轴电缆贵、不好施工、还是一根线上挂所有设备,双绞线把这些短板都补上了。双绞线分 UTP 非屏蔽和 STP 屏蔽两类,把两根线按一定绞距绞合起来,是为了让外界干扰在两根线上感应出的噪声尽量一致——接收端做差分,把两根线的电压相减,共模的干扰就被抵消掉了。不同线对用不同绞距,是为了减少线对之间的串扰。

5 类、6 类指的是双绞线的类型,类别越高,支持的带宽越高,对绞距、线径、串扰指标的要求越严。这里有个很多人踩过的细节:百兆以太网只用到两对线(1-2 和 3-6),千兆四对全用,而且每对双向同时收发。所以一根水晶头压错了两对线的百兆链路照样能通,换成千兆就死活协商不上——不是网卡坏,是物理层根本没凑齐四对可用的通道。

介质典型速率距离约束抗干扰主要短板
同轴电缆10Mbps单段 500 米较好共享介质、成本高、施工难
UTP 双绞线百兆/千兆约 100 米靠绞合与差分距离短、易受强电干扰
STP 双绞线百兆/千兆约 100 米更好,需正确接地屏蔽层接地不当反而更差
光纤远高于铜缆数百米到数公里完全不受电磁干扰施工与光电转换成本高

这张表里没有“谁更好”,只有“谁更适合当前约束”。同轴在只有它可选的年代是唯一解,双绞线靠便宜和易施工统治了桌面,光纤在距离和抗干扰要求一上来就变成刚需。介质选型从来不是技术先进性的比赛。

5. 冲突域与链路排查:五条踩坑记录

5.1 现象:人一多就卡,换台贵设备也没用

现象:一个小组的几台设备接在一个老式共享设备上,平时传文件挺快,只要两台以上同时传大文件,速度立刻掉到原来的几分之一,还伴随明显卡顿。换更高性能的主机、加内存,毫无改善。

原因:这几台设备还在同一个冲突域里,共享同一份介质带宽。设备数增加,冲突概率上升,CSMA/CD 的退避重传把大量时间花在等待上,有效吞吐随节点数增加而下降。这跟主机性能一点关系都没有,瓶颈在物理层的介质共享方式。

解决:把共享式设备换成交换式设备,让每个端口成为独立的冲突域,配合全双工,收发各走一对线,冲突检测都不需要了。如果暂时不能换设备,就减少同一共享段上的节点数,或者把大流量设备单独分段。判断方法很朴素:看设备的冲突计数、看链路是半双工还是全双工,比看 CPU 占用有用得多。

5.2 现象:百兆正常,千兆怎么都协商不上

现象:同一根网线,接百兆设备一切正常,接千兆设备要么协商成百兆,要么链路反复 up/down。

原因:千兆需要四对线全部可用,百兆只用两对。水晶头压接时有一对线序错、某对线断芯、线缆类别不够(比如用了老的低类别线),百兆仍然能跑,千兆直接失败。这类问题最迷惑人,因为“能通”给了你一切正常的错觉。

解决:用测线仪把四对线全部测一遍,不要只测通断;按统一的线序标准重打水晶头;确认线缆类别和链路速率匹配。千兆链路上,任何一对线的质量都会拖垮整条链路,这一条是布线阶段就要守住的红线。

5.3 现象:链路时通时断,丢包率忽高忽低

现象:两台设备之间 ping 时断时续,延迟偶尔飙到几百毫秒,抓包能看到大量 CRC 错误和冲突统计。

原因:常见的是双工模式错配——一端强制全双工,另一端自协商,自协商失败后退成半双工,一端认为可以同时收发,另一端还在做冲突检测,结果就是持续的冲突和丢包。另一个常见原因是线缆或接头接触不良,属于纯物理层的间歇性故障。

解决:两端统一策略,要么都自协商,要么都强制成相同的速率和双工,不要一半一半;再检查接头、走线是否靠近强电、线缆有没有被压折。排查顺序上,先看链路协商结果,再看错误计数,最后才怀疑上层协议。

5.4 现象:线路拉得越长越不稳,短一点就好

现象:设备放在同一房间一切正常,拉到楼下就开始丢包,换短一点的线又恢复正常。

原因:介质有距离预算。同轴电缆单段 500 米、双绞线约 100 米,这些数字背后是衰减和冲突检测时间窗口的双重约束。超长之后,信号到达对端时已经衰减到难以判决,或者冲突信号来不及在帧发完前返回,发送方误判为成功,重传机制又掩盖不了底层的错误。

解决:按标准的距离预算布线,超长就加中继设备、分段,或者直接换光纤。别指望调大重传次数能救回来——物理层已经丢了的信息,上层重传只会让情况更糟。做嵌入式设备时尤其要留意,很多模组的标称距离是在理想条件下测的,实际布线要留余量。

5.5 排查顺序:从下往上,别一上来就怀疑协议栈

网络问题最怕的就是从上往下猜。合理的顺序是:先看物理层,链路灯亮不亮、协商出来的速率和双工是多少、有没有 CRC 错误和冲突计数;再看链路层,MAC 能不能学到、广播域是不是被放大;最后才看网络层及以上,地址、路由、端口。这个顺序的依据就是前面几章讲的封装层次——下层不通,上层的所有配置都是空的。把错误计数当成黑匣子里的第一手证据,比反复重启设备有效得多。

6. 介质选型的取舍:同轴、双绞线、光纤不是简单的替代关系

把三种介质按时间顺序排,很容易得出“新的淘汰旧的”这种结论,但真正的工程决策不是这么做的。同轴电缆在共享型网络时代是唯一可行解,一根线挂多个设备,靠耦合器分接,成本在当时可接受,代价是共享介质带来的冲突和毫无私密性可言;双绞线用绞合和差分把抗干扰做进了线材本身,便宜、柔软、易施工,代价是距离被压到百米级,而且千兆必须四对全用;光纤把电磁干扰这个变量彻底删掉,距离和带宽都上了一个量级,代价是光电转换、熔接、施工门槛。

约束条件更合适的选择原因
成本敏感、距离短、节点少双绞线线材和施工便宜,维护简单
强电磁环境、长距离光纤不受电磁干扰,衰减小
历史遗留的共享段同轴或加中继改造成本高时先做分段
高带宽、低时延要求光纤优先带宽上限高,传输稳定

我印象最深的一次教训,是拿着一根“能通就行”的网线去跑千兆,反复怀疑网卡驱动,最后用测线仪一测,四对线里有一对根本没压好。从那以后,我每次碰链路问题都强制走一遍:先看协商速率和双工,再测四对线,最后才动协议栈配置。物理层不会骗人,它只是不说话。希望帮到你。

本文还有配套的精品资源,点击获取

返回列表