简介:《网络基础物理层详细讲解》是一份面向网络入门者、嵌入式开发与以太网/TCP/IP初学者的小型技术文档,聚焦数据封装与解封装全流程,以及电路交换、报文交换、分组交换的差异与适用思路。内容结合快递包裹类比说明上下层封装关系,并延伸到网线、网卡、协议栈组成最小网络的三要素,系统梳理同轴电缆、双绞线、光纤等传输介质,10BASE5以太网标准、共享型网络、冲突域及CSMA/CD先听后发、边听边发、随机延迟重发等关键概念,帮助读者建立从比特流到协议栈的物理层认知。压缩包共1个docx文件,约3.46MB,便于集中阅读和做笔记。已有106人学习,适合用来打牢网络基础、理解数据交换与物理介质选型,也可作为嵌入式网络调试前的概念梳理材料。
1. 从一根共享的同轴电缆说起:物理层真正要解决的三个麻烦
很多讲网络基础的材料从 TCP/IP 四层模型切入,一上来就是三次握手和子网掩码,物理层只留一句“传输比特流”就带过。可真正让人抓头的往往是最底下这一层:一根同轴电缆上挂十几台设备,谁发数据全都能听见,私密性等于零,它照样把 Ethernet 跑起来了;换成双绞线之后,八根线里少接两根,千兆网卡直接协商成百兆。物理层不负责“可靠”,它只负责比特能不能从一端抵达另一端,其余语义全部丢给上层。嵌入式设备上 TCP/IP 协议栈几乎是标配,越是这种“默认就有”的东西,越值得把它的前因后果拆开看。
2. 三种交换方式:电路、报文、分组各自在赌什么
数据要从 A 走到 B,中间那根线归谁用、怎么用,历史上试过三条路。三条路的差别不在“快慢”,而在它们愿意为什么付出代价:电路交换赌线路稳定,报文交换赌标识够用,分组交换赌切片带来的开销能被公平性抵消。把这三者摆在一起看,后面封装、冲突域、CSMA/CD 这些机制才有落脚点。
2.1 电路交换:先占线,再说话
电路交换的思路最接近打电话。通信开始之前,两端之间要先维护出一条逻辑电路,这条线路只传递这一对设备的数据,别人的数据一律进不来。好处很直观:路径固定、时延稳定、到达顺序天然有保证,中间不需要任何标识字段去区分“这块数据是谁的”,因为线本身就是专属的。
代价同样直观。这条逻辑电路一旦建立,哪怕某一时刻双方都不说话,它也依然被占着,别的设备想用这条线传数据只能干等。对早期以语音为主的场景,人说话本来就有一半时间是静默的,这种浪费尚可忍受;一旦换成计算机之间突发性的数据交互,占线不传就等于把线路利用率按在地上摩擦。
这里藏着一个容易被忽略的设计取舍:电路交换把“区分数据归属”这件事交给了物理线路本身,而不是数据包里。省掉了报头开销,换来的是线路无法复用。后面两种交换方式,本质上都是在想办法把这部分“归属信息”从线里挪到数据里去。
2.2 报文交换:给每份数据贴上“身份证”
当两台以上设备想同时用同一条线路发数据时,问题立刻变成“怎么防止数据在传输过程中串味”。报文交换的解法很朴素:每台设备把自己要发的数据加上一个标识,接收端根据标识判断这段数据从哪来、到哪去,这样多台设备复用同一条链路也不会把数据搅混。
这个思路的关键在于“存储转发”——中间节点先把整份报文收下来,看清标识,再决定往哪个方向送出去。线路不再被某一对通信独占,一段一段地被不同报文轮流使用,利用率比电路交换高出一截。
但报文交换有个天生的软肋:整份报文是一个不可分割的整体。一份大报文占着线路的时候,后面排队的报文只能等它全部传完。报文多大,占线就多久,时延完全不可控。在文件传输场景里这可能只是“慢一点”,在交互式场景里就是“卡住不动”。
2.3 分组交换:为什么必须把数据切成固定大小的片
分组交换保留了报文交换的标识机制,但多做了一件事:给每份数据的大小定一个上限,先做分片,每个数据段的值不超过这个上限,同时给每个分片加上标识,说明它从哪个设备来、到哪个设备去。多台设备共用一条链路,靠标识区分归属,靠分片保证单次占线时间有界。
到这里,“数据紊乱”和“某份数据长期霸占线路”两个问题同时被压住了。原文里提到分组交换之后出现的“转发不公平性”——数据大小不一致时,大块数据天然占便宜——正是靠“规定一个固定值、每次只传这么大”来削平的。切片把一条长数据拆成等长的段,谁也无法靠体量大来插队。
代价是开销。每个分片都要带一份报头,分片越小,报头占比越高。一份 100 字节的净荷配 20 字节报头,开销就是 20%;如果把分片再切小一半,这个比例直接翻倍。所以分片大小的选择从来不是“越小越公平”,而是在公平性、时延上界和报头开销之间找平衡点。
用一个简化模型把切片和重组的过程摆出来:
MAX_PAYLOAD = 1024 # 每个分片能携带的净荷上限(字节),决定单次占线的时长上界 def fragment(payload: bytes, src: str, dst: str): pieces = [] seq = 0 # 按固定步长切片,保证每个分片大小一致,转发时谁也不能靠体量占便宜 for offset in range(0, len(payload), MAX_PAYLOAD): chunk = payload[offset:offset + MAX_PAYLOAD] # 每个分片都独立携带源/目的标识与序号,接收端据此判断归属并重组 header = {"src": src, "dst": dst, "seq": seq, "length": len(chunk)} pieces.append((header, chunk)) seq += 1 return piecesMAX_PAYLOAD是这套机制的核心参数:它决定了单个分片最多占用链路多久,也就决定了其他设备最长要等多久。src和dst对应报文交换里那层“标识”,解决的是多设备复用同一链路时的归属问题;seq解决的是乱序到达后的重组问题。真实的 IP 分片用的是偏移量加“还有没有后续分片”的标志位,字段设计不同,但要解决的矛盾是同一组。
三种交换方式的对照可以拉成一张表:
| 维度 | 电路交换 | 报文交换 | 分组交换 |
|---|---|---|---|
| 通信前是否建立连接 | 是,独占逻辑电路 | 否 | 否 |
| 线路空闲时能否被他人使用 | 不能 | 能 | 能 |
| 单次占线时长 | 整个通话期 | 整份报文长度 | 固定分片长度 |
| 时延特征 | 稳定、可预期 | 波动大 | 有上界、可统计 |
| 主要开销 | 线路空置浪费 | 大报文阻塞 | 报头开销 |
真正值得记住的不是这三行定义,而是它们各自把“公平”寄托在哪里:电路交换寄托在物理独占上,报文交换寄托在标识上,分组交换寄托在切片上。后面物理层要解决的冲突问题,本质上是同一类矛盾在电气层面的重演。
3. 封装与解封装:一个快递包裹为什么被套了四层壳
封装这件事用快递来类比最贴切:包装盒里塞了泡沫、填充物、说明书,收件人真正要的只是里面那个小物件。小物件相当于应用层的数据,外面的每一层包装都只为运输服务,到了终点被一层层拆掉。发端从上往下套壳,收端从下往上拆壳,这个方向性不能反,反了整个体系就散了。
3.1 发端:每下一层加一个头
数据从应用层产生时只是一段有业务含义的字节流,传输层给它加上端口号一类的头部,网络层再加地址头部,数据链路层再套上帧头和帧尾,到物理层就只剩一件事:把这一整串变成 0 和 1,通过网卡和端口送进传输介质。
每层加头的时候,都有一个共同的约定:只关心自己这一层需要的字段,不对上层内容做任何解读。传输层看到的是端口,网络层看到的是地址,链路层看到的是相邻一跳的标识和校验。这种“互不越界”的安排,是分层能成立的前提,也是排障时能用“逐层剥开看”的前提。
def encapsulate(app_data: bytes, src_mac: str, dst_mac: str) -> str: # 传输层:加端口信息,此时仍是一串字节 transport = b"[PORT]" + app_data # 网络层:加地址信息,形成分组 network = b"[ADDR]" + transport # 链路层:加帧头与帧尾校验,形成帧 frame = b"[MAC]" + network + b"[CRC]" # 物理层:不再有结构,只有电平与比特 return "".join(f"{byte:08b}" for byte in frame)encapsulate的返回值是纯 0/1 字符串,这一步对应“数据变成比特流进入介质”。注意函数里每一层的拼接顺序:后加的头部永远在外面,拆的时候才能按相反顺序剥。CRC放在帧尾而不是帧头,是因为发送端要等整帧内容算完才能得出校验值,这个位置安排本身就是实现约束的产物。
3.2 收端:为什么必须一层层剥
接收侧的动作是发端的镜像,但多了一件发端没有的事:校验。链路层先核对帧尾的校验值,对不上就整帧丢弃,根本不会往上递。校验通过才剥掉帧头,把剩下的内容交给网络层;网络层看完地址确认是发给自己的,再剥一层交给传输层;传输层按端口找到对应进程,最后应用层拿到的才是原始数据。
这个顺序不能跳。跳过链路层直接看网络层内容,等于在一个还没确认完整的字节流上做解析,出错时连“是哪一跳坏的”都定位不了。排障时之所以能从物理层一路往上查,靠的就是每层都留了自己的痕迹。
顺带说一个容易混淆的点:封装是发端行为,解封装是收端行为,两者在同一台设备上可能同时发生。一台中间设备收到帧、剥到网络层、做出转发决策、再重新封装成新的帧发出去——这不是“解封装没做完”,而是逐跳转发的正常形态,帧头在每一跳都会被换掉,地址信息却不是端到端不变的。
3.3 最小网络三要素的边界
原文把“网线、网卡、协议栈”称为完成最小网络的三要素,这个归纳很准,而且三者的分工恰好对应封装的三个阶段:协议栈负责按层加头拆头,网卡负责把帧变成线上的电平信号并做串并转换,网线负责把信号送到对端。少任何一环,比特流都走不完全程。
嵌入式场景里,协议栈通常是集成在设备上的,只要买的设备跟网络相关,基本都带 TCP/IP 支持。这带来一个副作用:开发者容易把“能 ping 通”当成“物理层没问题”,而实际上网卡协商、线序、介质规格这些底层问题,往往被上层协议的重传机制掩盖,直到流量一上来才暴露。
4. 以太网与冲突域:CSMA/CD 怎么把抢线变成礼让
共享介质上的多设备通信,电气层面会遇到一个绕不开的麻烦:两台设备同时往线上送信号,电压叠加,双方的数据都会变成一堆无法还原的噪声。CSMA/CD 就是为这个麻烦设计的规则集,它不消除冲突,只让冲突的代价可控。
4.1 10BASE5 这个命名里的信息量
早期以太网标准10BASE5的命名把关键参数全塞进了几个字符里,拆开看很能说明物理层的设计约束:
| 字段 | 含义 | 对应的设计约束 |
|---|---|---|
| 10 | 每秒 10 兆比特 | 决定编码时钟与线缆带宽需求 |
| BASE | 基带传输 | 整根线只传一路信号,不做频分复用 |
| 5 | 传输距离 500 米 | 信号衰减与冲突检测窗口的上限 |
“基带”两个字值得多说一句:整条介质在同一时刻只承载一路信号,没有频段划分,所以任何两台设备同时发就是硬碰硬的叠加。这也是冲突必然发生的物理根源——不是协议没设计好,是介质本身只给了一条道。
这个标准在同轴电缆上使用曼彻斯特编码传输。曼彻斯特编码的特点是每个比特位的中间都有一次电平跳变,跳变方向表示 0 或 1。代价是所需带宽翻倍,收益是信号自带时钟——接收端可以从跳变本身恢复出位定时,不需要额外传一路时钟线。用带宽换同步,这是物理层里非常典型的一笔交易。
4.2 冲突域:共享介质的物理宿命
同轴电缆要实现多设备互联,可以借助耦合器,每个接口下接一台设备,画出来就是一根线承载多个节点。这种结构叫共享型网络,所有设备共享同一根网络线。信号在线上传播时会覆盖整个介质范围,任何两个节点同时发送,信号就会在某个位置叠加,这个会发生碰撞的范围就是冲突域。
冲突域的大小直接决定网络表现:设备越多,同时发送的概率越高,冲突越频繁,有效吞吐越低。而且共享型网络没有私密性可言——线上任何一台设备都能收到别人发的信号,只是网卡在地址不匹配时选择不往上递而已。
这里有个容易被误读的因果:不是“设备多了所以慢”,而是“设备多了冲突概率上升,重传次数增加,线路时间被浪费在无效传输上”。理解了这层,才能明白为什么后来要把冲突域一点点缩小,直到缩成点对点。
4.3 CSMA/CD 的四步节奏
载波侦听多路访问/冲突检测,名字很长,动作只有四步,顺序不能乱:
第一步,先听后发。设备发送前先去听线上有没有电压摆动值。如果线是静止的,说明当前没人发,可以开始发送。这一步的关键是“听”的物理实现——侦听的是介质上的电平变化,而不是某种逻辑信号。
第二步,边听边发。发送过程中持续侦听,因为两个设备可能同时听到线是静止的,然后同时开始发送。冲突不可避免,只能尽早发现。
第三步,冲突停发。一旦侦听到线上出现异常电压摆动,说明自己的数据和别人的撞上了,立刻停止发送,并发出一个强化冲突的信号,让网络上其他设备也尽快感知到这次碰撞。继续发下去只会浪费更多线路时间。
第四步,随机延迟后重发。停下来之后不能马上重试,否则两个刚撞过的设备大概率会再次同时发送。设备计算一个随机延迟值,在这段时间内不往线上发,时间到了再回去听线上是否仍被占用。
四步里最容易被低估的是第三步的“立即停”。它把一次冲突的代价从“整帧传完才发现白传”压缩到“撞上就止损”,这是共享介质能把利用率维持住的关键。
4.4 退避算法:随机延迟为什么不可省
退避的核心是“随机”。如果两个设备冲突后都等固定时间再发,它们会再次同时发送,形成无限循环的碰撞。随机值把两者的重发时刻错开,冲突才有可能被打破。
常见的实现思路是二进制指数退避:第 n 次冲突后,从 0 到 2^n - 1 个时间槽里随机取一个值作为等待时长。冲突次数越多,可选窗口越大,等待时间的期望值也越大,网络越拥挤时退让越明显。
import random SLOT = 51.2e-6 # 一个时间槽的时长,与网络速率和最大冲突域直径相关 def backoff(retry: int, max_retry: int = 16) -> float: # 冲突次数超过上限直接放弃,避免一个故障节点无休止重试拖垮全网 if retry > max_retry: raise TimeoutError("retransmission limit exceeded") # 窗口指数增长,但封顶在 10,防止等待时间无限膨胀 k = min(retry, 10) slots = random.randint(0, 2 ** k - 1) return slots * SLOTretry是当前已经历的冲突次数,k是参与随机取值的位数,封顶在 10 意味着窗口最大 1023 个时间槽。SLOT的取值不是随便定的,它要保证最远端的设备也能在冲突信号传回之前完成检测——这正是 10BASE5 里那个“5”(500 米)与时间槽之间的隐含关联:距离越远,往返时间越长,时间槽必须跟着放大,否则冲突检测会失效。
max_retry存在的意义也值得说一句:没有它,一个网卡故障或线缆短路导致的持续冲突会让某台设备永远在重试,把整条线路的有效带宽吃干净。上限是一种保护机制,代价是超过上限后这帧数据被丢弃,交给上层去处理。
4.5 双绞线与光纤:把冲突域关进点对点里
同轴电缆贵、共享、冲突多,这三个缺点共同推动了介质演进。双绞线分非屏蔽和屏蔽两类,5 类、6 类指的是双绞线的类型,决定了能承载的带宽与抗干扰能力。这里有个常被忽略的细节:双绞线如果是千兆速率,八根线全部都要用上;百兆只用到其中四根。这就解释了为什么“线能通但速率上不去”——少接的几根线不会被上层协议察觉,只会体现在协商结果上。
光纤则把冲突问题从根上绕开了。点对点连接意味着一条链路只有两端,不存在第三方同时发送的可能,冲突域退化成“两个端口之间”。配合交换式设备,每个端口是一个独立的冲突域,CSMA/CD 在双工模式下不再需要启用。
从共享同轴到点对点双绞线再到光纤,这条演进线的本质不是“线变好了”,而是“冲突域被越切越小”。当冲突域小到一个端口只连一台设备,冲突检测这套机制就完成了历史使命。
5. 物理层排查:五类“时通时断”背后的真实原因
物理层的故障有个共同特点:上层协议的重传机制会把间歇性错误吸收掉,表现出来的往往是“能通、但慢、偶尔断”,于是排查方向容易被带偏到应用层。下面五条是按现场出现的频次整理出来的,每一条都按现象、原因、解决三段走。
5.1 千兆链路协商成百兆
现象:两端网卡都标称千兆,交换机端口灯也亮,实际传输速率只有百兆,大文件传输时间长了一截,抓包看不出丢包。
原因:双绞线八根线中只接了四根,或者线序在制作时压错。百兆只需要两对线,千兆需要四对线同时收发,缺一对就只能在低速率上协商成功。
解决:用测线仪逐对核对八根线的连通性和线序,重点看 4、5、7、8 这四根是否到位。千兆场景下不要图省事只压两对。这类问题不会报错,只会静默降速,属于典型的“黑匣子”式故障。
5.2 同轴网络时通时断
现象:共享型网络在设备少的时候一切正常,接入的设备一多就开始丢包,重启某台设备后短暂恢复,过一会儿又不行。
原因:同轴电缆两端需要终端匹配来吸收信号反射,终端缺失或耦合器接头氧化都会让信号在末端反射回来,与正常信号叠加形成伪冲突。设备越多,叠加概率越高。
解决:先确认总线两端都装了匹配终端,再逐个检查耦合器接口是否有氧化、松动。用替换法把可疑分支摘掉观察,比在整条总线上盲猜快得多。
5.3 共享型网络一多人用就卡
现象:白天办公时段网络明显变慢,晚上或者只有一两个人时速度正常,ping 网关的时延忽高忽低,但不断线。
原因:冲突域过大。所有设备共享同一根介质,并发一高,冲突次数按平方级上升,大量线路时间被消耗在“撞了停、停了等、等了再撞”的循环里,有效吞吐远低于标称带宽。
解决:这不是配置问题,是拓扑问题。把共享介质拆成交换式的点对点连接,或者分段缩小冲突域,才是根治手段。在共享型网络上做带宽限制、QoS 调优,收益都很有限。
5.4 两端速率协商不一致
现象:链路能起来,但吞吐抖动严重,重传计数持续上涨,换一根线或者换一个端口有时候就好了。
原因:一端强制指定速率和双工模式,另一端保持自协商。自协商依赖链路脉冲交互,一端不参与协商时,另一端会退到一个“半双工、低速”的保守结果,两端对双工模式的理解不一致,就会出现大量冲突和错帧。
解决:两端要么都自协商,要么都手工指定成同一组参数,不要一端强制一端自动。这类配置不一致在现场极常见,血泪经验是:动过一端配置,另一端必须同步核对。
5.5 超过介质规格距离后开始丢包
现象:链路短距离测试完全正常,拉到设计长度后出现零星丢包,误码率随距离增加而上升,环境温度高的时候更明显。
原因:信号在介质中传输会衰减,超过规格距离后信噪比不足以支撑正确判决。10BASE5 的 500 米、双绞线的百米级限制,都是按最坏情况留了余量的硬指标,不是建议值。
解决:不要靠调大重传次数来“扛”,那只是把物理层的问题转嫁给上层。要么缩短距离加中继,要么换用衰减更小的介质。施工时预留的余量,就是给自己留的后悔药。
6. 设计取舍:物理层为什么甘愿做一个“无知的搬运工”
物理层最反常识的一点是它的“无知”。它不知道这串比特是网页、是文件还是一段语音,不关心地址,不关心顺序,只负责把 0 和 1 变成线上的电平变化送出去。这种刻意的无知不是能力不足,而是分层体系里最划算的一笔交易:正因为物理层不解读内容,上层换协议、换业务、换应用时,底下这条线可以一动不动。
同样的取舍思路贯穿整条链路。曼彻斯特编码用双倍带宽换来自带时钟的同步能力,省掉了单独的时钟线;分组交换用固定分片换来时延上界和转发公平性,代价是每个分片都要背一份报头;共享同轴用布线成本换来的是无私密性和冲突频发,最终被点对点双绞线取代。每一个“缺点”在当时都有它换到的东西,判断一个设计好不好,要看它换来的那部分是否值得。
验证这套理解是否到位,有个简单方法:拿一条实际链路,从下往上逐层确认。物理层看协商速率、双工模式、介质规格距离;链路层看帧校验错误计数;网络层看分片与重组是否正常;传输层看重传率。任何一层出现异常计数,就停在那一层往下查,不要跳着猜。这个习惯比记住任何一条命令都有用。
我自己的教训来自一次千兆降速的排查:当时从应用层一路查到传输层,抓包、看重传、换端口,折腾了大半天,最后测线仪一夹,八根线里只通了四根。从那以后我每次接线上电前,都强制先跑一遍线序和协商速率的确认,再谈上层配置。希望帮到你。
本文还有配套的精品资源,点击获取