
如果你用过Wi-Fi大概率遇到过这种场面手机显示信号满格、宽带套餐也不低但一到晚高峰视频会议开始马赛克文件上传像蜗牛爬。很多人第一反应是路由器不行或者运营商限速折腾一圈后发现问题很可能出在“空气本身太吵了”。无线局域网里所有设备共享同一片无线电频谱谁先说话、说多久、撞车了怎么处理全靠一套规则来维持秩序这就是CSMA/CA——载波监听多址接入/冲突避免协议。它是802.11无线网络的介质访问控制基础也是理解Wi-Fi一切“玄学问题”的钥匙。这篇文章想把这套协议的来龙去脉、底层机理、典型坑位和实操观察方式一次讲透适合网络工程师、嵌入式物联网开发者以及所有被Wi-Fi问题折磨过的运维朋友。1. 从“边说边听”到“先听后说”无线网络为什么不能照抄以太网1.1 有线以太网的老规矩CSMA/CD上世纪七八十年代以太网还跑在粗同轴电缆上所有设备挂在一根总线上结构跟一条晒衣绳晾了一排衣服差不多。那时候的介质访问控制协议叫CSMA/CD全称是载波监听多址接入/冲突检测。这套规则听起来很直白设备想发数据先听信道里有没有别人在发如果信道空闲就发出去发送的同时继续监听如果发现自己发出的信号和别人的信号在线上“撞”了就立即停止然后等待一个随机时间再重试。核心思路是“边说边听、撞了就跑”属于事后补救型协议。为什么有线网络做得到因为总线上每个节点都能同时收发发送信号的强度和接收信号的电平是可以比较的。一旦检测到总线上的信号比自己发出去的电平高、或者波形出现畸变就能立刻判断发生了冲突。这是有线介质的物理特性带来的天然优势。1.2 无线信道里的三个物理现实到了无线环境这套老办法彻底失灵了原因有三个。第一半双工的天生缺陷。无线网卡在发射时天线处于大功率输出状态接收通路会被自己的发射信号淹没。你根本不可能做到“边发边听”就像你不能一边扯着嗓子喊话一边听清对面蚊子般的耳语。要同时收发得做专门的自干扰消除电路那属于全双工无线通信的范畴传统802.11设备根本不具备这个能力。第二信号覆盖范围不相同。有线总线上的信号是“一条线”传播所有节点对信道状态的感知基本一致。无线电信号在空气中衰减A能听到B但C可能完全听不到B节点之间对“信道忙不忙”的判断常常不一致。第三冲突代价完全不同。有线以太网上发生冲突顶多是一段电信号作废重新发一次就行。无线环境里如果两个帧在空中撞在一起接收端的信号直接变成噪声而且发送方自己还不知道只能靠超时等待ACK来判断。一次冲突不仅浪费了发送时机还白白消耗了射频资源。1.3 所以802.11选择了另一条路避免冲突既然没法低成本地“检测”冲突那就换一个思路让冲突尽量不要发生。这个思路转化成了CSMA/CA的三个核心动作先听——发送前确认信道是否空闲让——信道空闲后还要再等一段固定时间退——如果信道本来就在忙或者自己刚撞过车就随机退避一段时间再试。简单说以太网的红绿灯逻辑是“闯了红灯被拍了再罚”Wi-Fi的红绿灯逻辑是“宁可多等一会儿也别闯红灯”。这就是CSMA/CA与CSMA/CD最本质的区别。理解了这个背景后面每一个具体参数的设计都能找到理由。2. 载波侦听、帧间间隔与随机退避CSMA/CA的三道闸门2.1 第一道闸门物理载波侦听与虚拟载波侦听CSMA/CA里的“载波侦听”不是一句空话它由两层机制组成。物理载波侦听由无线网卡的射频前端完成也就是常说的CCA信道空闲评估。网卡会持续测量当前信道上的接收信号能量若能量高于一个阈值典型值在-82dBm左右就判定信道为忙。这个机制能识别出Wi-Fi信号也能识别出微波炉、无线摄像头等非Wi-Fi干扰源——只要是能量也能触发忙判断。但是仅靠物理侦听不够还有一个虚拟载波侦听机制核心是NAV网络分配向量。802.11帧头里有一个Duration字段它告诉周围所有站点“我这轮传输要占用信道多久”。任何一个站点收到别人的帧看到Duration字段后就会在自己的NAV计时器上设置对应的时间。只要NAV没归零即使物理信道其实很安静站点也会保持沉默。打个比方物理侦听相当于你亲眼看门口有没有人虚拟侦听相当于你听别人说“我约了五分钟后的会议室”虽然现在还没人走进会议室你也乖乖在外面等着。这两层机制合在一起构成了CSMA/CA最基础的“先听后说”。2.2 第二道闸门DIFS等待假设一个站点做完物理载波侦听发现信道空闲是不是马上就能发帧还不行它必须让信道持续空闲一段规定时间这个时间叫做DIFS分布式帧间间隔普通数据帧在发送前必须等待这么久。DIFS存在的意义很实际如果所有站点一看到信道空闲就立刻扑上去概率上必然会有多个站点同时发送那就又撞车了。DIFS相当于一场酒会里的“礼貌距离”规则——前一位演讲者刚说完大家不能立刻抢话筒得先安静片刻让可能存在的、优先级更高的回复先飞出去。这个“优先级更高的回复”指的是ACK、CTS这类对上一帧的即时应答。它们的等待时间更短叫SIFS比DIFS短得多所以它们总能抢在下一轮新数据传输之前完成。DIFS的数值也因此在历史上被设计为SIFS加上两倍时隙长度目的就是拉开与确认帧的时间档次。2.3 第三道闸门随机退避计数器DIFS等了信道也是空闲的但还是可能有好几个站点同时等完DIFS同时开抢怎么办这时候轮到随机退避出场。每个站点在DIFS之后会从0到竞争窗口CW之间随机抽一个整数作为自己的退避计数器。只有在信道保持空闲时计数器才会逐时隙递减只要信道状态变成忙计数器立刻冻结等信道重新空闲并且再次满足DIFS后再继续倒数。谁先倒数到0谁就赢得发送权。如果很不幸两个站点抽到了相同的数字并且同时倒数到0它们的帧还是会碰撞。但碰撞之后802.11会执行二进制指数退避竞争窗口CW翻倍。假设初始CW范围是0到15第一次碰撞后变成0到31第二次变成0到63一路翻到最大值CWmax2.4G/5G常规取1023。窗口越大两个站点抽到同一个数的概率越低也就给整个网络时间“缓过劲来”。这个机制特别像一群人挤一扇窄门第一次所有人都抢着冲出去结果卡在门口于是大家后退一步心里默数一个随机的数数到0的人再走。越是连续卡住大家后退得越远、默数的范围越大后面反而越来越顺畅。这正是CSMA/CA的随机性精髓——用可控的等待时间换取极低的碰撞概率。3. 空气语言最难翻译的方言隐藏节点与暴露节点3.1 隐藏节点你听不见的敌人CSMA/CA最著名的盲区就是隐藏节点问题。想象一个场景节点A和节点C都能听到接入点AP但由于距离原因A和C互相听不到对方。现在A准备向AP发数据它做了载波侦听发现信道空闲——因为C的无线信号根本就传不到A的接收机这里。于是A放心发送几乎同时C也做了载波侦听也判断信道空闲也开始发送。结果两个帧在AP处撞在一起AP啥也解不出来。A和C各自都没错它们都遵守了“先听后说”但因为“听不见对方”在公共接收点AP那里造成了冲突。这就好比两个人都看不见对方却同时对着同一个话筒说话话筒前的人一句话也听不清。这个问题的麻烦之处在于它没法靠优化CCA阈值彻底解决。就算把阈值调得很低让A勉强侦听到C的微弱信号——但A收到的只是一点噪声级能量根本没法判断那是不是一个正在发送的有效帧。隐藏节点效应是无线分布式接入机制的天然缺陷。3.2 暴露节点错让的传输机会与隐藏节点对应的是暴露节点问题。场景换一下节点B正在向节点A发送数据节点C位于B的覆盖范围内所以C的物理载波侦听是能感知到B在发送的。但C想发给的节点D呢D在C的另一侧根本不在B的覆盖范围内——也就是说B和C同时发送完全不会互相干扰。按照CSMA/CA的规则C检测到信道忙就会老老实实退避哪怕它本来可以安全地向D发送。这就是暴露节点问题C被“虚惊”拦住了白白浪费了一次可用的并发传输机会。和隐藏节点相比暴露节点问题的危害不那么致命它损失的是信道利用率而不是数据正确性。但它在密集部署场景里会显著压缩系统吞吐量是无线网络从“能用”到“好用”必须面对的问题。3.3 为什么这两个问题催生了RTS/CTS解决隐藏节点问题最直接的办法是让发送方把自己的传输意图“广播”到所有可能涉及的站点。单个站点覆盖不到的地方就让接收方来补位。这就是RTS/CTS握手的出发点。RTS请求发送由发送方发出CTS清除发送由接收方回复。这两帧的特点是短小、以最低速率传输但携带了完整的Duration信息。凡是能听到RTS的站点以及凡是能听到CTS的站点都会被NAV机制约束住在预约时间内保持静默。于是隐藏节点虽然听不到A的RTS但它能听到AP回给A的CTS就被拉进了“知情圈”。这本来看上去是个完美的补丁但实际工程里所有补丁都有代价RTS/CTS的开销问题我们放到下一章细说。4. RTS/CTS握手让看不见的邻居知道你要说话了4.1 握手四步流程与NAV链条RTS/CTS的完整交互流程是四步握手加一个确认发送方发RTS帧里面包含接收方地址、发送方地址以及Duration字段Duration的数值按“后续所有帧加三个SIFS间隔”的总时长来填。接收方收到RTS后回发CTS帧CTS里的Duration 原RTS剩余传输时间减去已消耗的SIFS和CTS本身所需时间。发送方收到CTS后确认信道已预约成功开始发送数据帧。接收方收完数据帧经过一个SIFS回发ACK确认。这四步里的每一步都与NAV联动。举个具体例子站点X听到A发出的RTSDuration填的是200微秒X就把自己的NAV设成200微秒这之后哪怕物理信道安静得要命X也得闭嘴等到NAV归零。另一个站点Y没听到RTS但听到了AP回发的CTSY同样会设置自己的NAV。RTS和CTS的覆盖范围叠加就编织出一张覆盖“发送方可达域接收方可达域”的静默网。这就是RTS/CTS的精妙之处——它不是靠提高发射功率、也不是靠让隐藏节点额外传输信息而是利用最短的两帧把占用信道的意图广播给所有可能捣乱的邻居。代价则是每一次数据发送前都要额外花掉两帧的时间。4.2 信道的“预订费”有多贵rts_threshold的取舍RTS/CTS不是免费的。发送一个RTS要花时间、对方回CTS又要花时间加上中间的SIFS一套握手下来差不多要占用几十到上百微秒。如果数据帧本身很长这开销还能接受但如果数据帧很短握手开销占比可能超过30%极端情况下甚至出现“预约信道的成本比传数据还贵”的倒挂。所以实际设备里都实现了rts_threshold参数。只有当帧长度超过这个阈值时站点才启用RTS/CTS握手短帧则直接发省去握手开销。Linux下查看和设置rts_threshold的命令很简单# 查看当前RTS阈值 iw dev wlan0 get rts # 设置RTS阈值为256字节帧长超过256字节才走RTS/CTS iw dev wlan0 set rts 256不少网卡驱动的默认阈值是2347字节这基本等于“全部禁用RTS/CTS”因为普通帧很少超过这个长度。在家庭小环境里这么做没毛病但到了办公区、教室、仓库这类AP密度高、终端多的场所隐藏节点问题会非常突出如果你确定环境里有隐藏节点可以尝试把阈值调到500甚至256字节。代价是每帧都要多付“预订费”换来的是更少的碰撞和重传。实际效果需要现场测量不能一概而论。4.3 CTS-to-Self一个请求发送的特例RTS/CTS还有个很有意思的变体叫CTS-to-Self自CTS。它不经过RTS请求而是站点在发送高优先级帧、或者需要保护特定传输时直接自己发一个发给自己的CTS帧附近听到这个CTS的站点会被NAV约束住等于是“自助预约车道”。CTS-to-Self的经典应用场景是802.11g混合网络。当11g和11b设备混在同一AP下时11g的高速帧容易被11b的慢速长帧“踩死”。如果每个帧都做完整的RTS/CTS握手开销太大于是站点可以选择发一个CTS-to-Self用一帧广播替代两帧握手以较低的开销保护自己的传输窗口。很多网卡驱动在兼容模式下会自动启用这个机制。5. 帧间间隔的博弈论谁先说话由等待时间决定5.1 SIFS/PIFS/DIFS/EIFS时间就是话语权802.11协议里定义了好几档帧间间隔它们本质上是信道的“话语权分层”。SIFS短帧间间隔最短留给ACK、CTS这类对上一帧的即时应答。发送方发完数据接收方只等一个SIFS就立刻回ACK中间不许任何其他站点插嘴。因为其他站点要等更长的DIFS所以它们在时间上永远慢半拍。PIFS点协调帧间间隔比SIFS长一点等于SIFS加一个时隙长度主要用在PCF模式下的轮询响应以及某些AP发起的控制帧里。由于它比DIFS短AP的轮询信标能比普通数据帧优先获得信道。DIFS分布式帧间间隔是普通数据发送前的必须等待时间等于SIFS加两个时隙。EIFS扩展帧间间隔更长在站点收到无法解调的坏帧后使用目的是给可能正在重传的发送方留出足够的恢复时间。不同物理层的具体数值差异很大但结构关系是一致的物理层标准时隙长度SIFSPIFSDIFS802.11b2.4GHz20μs10μs30μs50μs802.11a/gOFDM9μs16μs25μs34μsPIFS永远是“SIFS加一个时隙”DIFS永远是“SIFS加两个时隙”这个递推关系本身就是优先级设计的骨架。谁等的时间越短谁就越接近“优先发言”信道访问权实质上是按等待时间拍卖出去的。5.2 WMM的四级优先队列有了SIFS和DIFS这套基本盘802.11e/WMM进一步把数据帧按业务类型分成了四个接入类别语音、视频、尽力而为、后台。每个类别都有自己独立的一整套退避参数包括AIFSN仲裁帧间间隔编号、CWmin和CWmax。接入类别AIFSNCWminCWmax典型场景语音AC_VO237语音通话视频AC_VI2715视频会议、直播尽力而为AC_BE3151023普通网页、文件传输后台AC_BK7151023下载更新、备份看到没有语音类数据的AIFSN只有2意味着它等待的时间更短CWmin只有3意味着它随机退避的范围非常窄基本是“稍等一下马上就发”。后台流量AIFSN是7竞争窗口大得离谱所以在信道拥挤时后台任务自然被压到最低优先级。实测中视频会议卡顿很多不是因为网速不够而是因为无线争抢时给出的优先级不够——这是完全可以在协议层面验证的事。5.3 时隙长度、覆盖半径和兼容性的隐形关联为什么802.11b要用20微秒这么长的时隙而11a/g用9微秒这背后其实藏着一个物理约束。时隙长度必须大于“信号从覆盖范围一端传到另一端再完成检测”的总时间。如果时隙太短远端站点还没来得及检测到信道上的传输信号近端站点的退避计数器就已经倒到0了碰撞率会飙升。802.11b当初为了兼容更大覆盖范围的室外场景把时隙拉长到20微秒换来了更强的抗路径延迟能力但代价是每个退避时隙白白耗电。5GHz频段天然以室内为主覆盖半径小9微秒就够了效率高得多。所以你看9微秒这个数字不是拍脑袋定的它是信号传播延迟、CCA检测时间、信道切换时间等一系列物理指标共同约束的产物。工程协议里最小的参数背后都压着一堆物理条件。6. 纸上协议落地到真实网络抓包观察与调优实验6.1 从普通网卡到Monitor模式准备一把“无线听诊器”CSMA/CA是看不见摸不着的参数都在网卡和AP的内部。要真正“看见”它工作最直接的办法是抓空口帧。默认情况下普通网卡只接收发给自己的帧抓包工具只能看到本机发收的数据。要观察全信道里所有设备的RTS、CTS、Beacon需要把无线网卡切换到Monitor模式。Linux下操作很简单# 停用网络管理对无线网卡的控制 sudo ip link set wlan0 down sudo iw dev wlan0 set type monitor sudo ip link set wlan0 up # 开始抓包保存为pcap文件 sudo tcpdump -i wlan0 -w wifi_capture.pcap有一点要提醒多数笔记本自带网卡并不支持Monitor模式或者天线增益太低导致抓到的帧极不完整。更靠谱的方案是用USB外接的无线网卡选择芯片在Linux下有原生驱动的型号而不是那些只支持Windows的“免驱卡”。抓包时把网卡固定放在目标AP附近一两米的位置效果会好很多。6.2 实战抓包找到RTS/CTS帧与Duration字段打开抓到的pcap文件在Wireshark里设置过滤条件就能筛选出CSMA/CA的核心机制帧# 只看RTS帧 wlan.fc.type_subtype 0x1b # 只看CTS帧 wlan.fc.type_subtype 0x1c # 只看ACK帧 wlan.fc.type_subtype 0x1d # 查看所有带重传标记的帧间接反映碰撞和退避 wlan.fc.retry 1抓包时你会看到非常有趣的现象。连续发送的数据帧之间空隙长度并不是等距的而是一会儿短一会儿长这就是随机退避的作用。信道繁忙时帧与帧之间会出现更大的间隙因为站点在等NAV归零。RTS帧里有个“Duration”字段CTS里也有你可以在Wireshark的“802.11 MPDU”信息栏里直接看到数值。把同一轮握手事件中的RTS、CTS、Data、ACK四个帧按时间排序你会发现它们之间的间隔恰好就是SIFS的长度而下一个无关站点发的帧要等到更晚才出现——NAV的约束效应在时间轴上清晰可见。6.3 观测信道占用定位“空气太吵”的真凶除了看协议帧我强烈建议你把Linux下的信道占用统计用起来# 查看当前网卡所在信道的占用情况 iw dev wlan0 survey dump输出里会有一组active time和busy time它们是网卡统计到的信道活跃时间和忙状态时间。用busy time除以active time就能估算出这个信道的实际占用率。如果busy time占比超过50%这个信道基本可以判断为“拥堵”。再往下查先看同频段里有多少个APAndroid上的Wi-Fi分析仪、Linux下用iw dev wlan0 scan都可以再确认环境中是否有微波炉这类非Wi-Fi干扰源。很多时候困扰你的“信号满格但网速差”根本不是宽带问题而是这个信道上的无效竞争太多数据帧送不出去。实战调优里有几种常用手段你可以按场景对号入座2.4GHz只选1、6、11三个不重叠信道同时把AP发射功率调低让同信道AP不互相踩踏。优先用5GHz频段5GHz信道多、干扰源少、时隙短同等环境下总线吞吐要高出一截。在隐藏节点明显的环境把rts_threshold从默认的2347降到512或256观察重传率是否能降下来。在设备密度极高的场景适当提高CCA阈值部分商用AP支持配置例如从-82dBm调到-70dBm让更多不在相互干扰范围内的设备实现空分复用但代价是弱信号设备更容易被边缘化。Wi-Fi 6时代协议层面又加入了BSS Coloring和OFDMA这类新工具它们本质上是给CSMA/CA“减负”BSS Coloring让工作站分清楚“这个忙状态是不是隔壁AP造成的”从而避免盲目退避OFDMA则让一个信道可以同时调度多个终端减少竞争碰撞的机会。但不管怎么演进CSMA/CA“先听、让、退”的底层逻辑依然是无线局域网秩序的核心。最后分享一个我自己的体会。前阵子帮一家中等规模的办公室排查无线网络问题二十几台终端、三个AP全都泡在2.4GHz。抓了一小时包发现信道1上的busy time常年超过65%大量帧都在无限重传。后来把其中两个AP的无线电切到5GHz2.4GHz只保留一个AP并把rts_threshold调低到512重传率肉眼可见地下降。这套排查方法没有任何玄学就是围绕CSMA/CA的机制逐一确认载波侦听、退避和冲突重传的每一个环节。遇到Wi-Fi“玄学”问题先问一句空气是不是太吵了往往比盲目换设备更有用。