很多人调无线网络的第一反应是看信号强度,只要终端上那几格不吃亏,就默认网络没问题。等真到了会议室坐满人、或者开放办公区大家同时开着视频会议,才会发现一个诡异的现象:RSSI 显示 -55dBm 相当漂亮,测速单机跑满,可一旦并发上来,延迟从 5ms 直接飙到 200ms,丢包和重传肉眼可见。这不是信号问题,也不是带宽问题,绝大多数时候是空口竞争机制在给你上课。而这个机制的名字,就是 CSMA/CA——载波监听多路访问/冲突避免。它是一整套运行在无线介质上的“发言规则”,决定了谁在什么时候能开口、说多久、说砸了怎么重来。搞清楚它,你才能明白为什么加信道不是万能药、为什么关掉低速率比换天线更有效、为什么小包业务天生吃不满空口。这篇文章面向的是需要真实排障的网络运维、嵌入式与物联网开发者,以及想搞懂 Wi-Fi 底层行为的爱好者,我会从协议设计动机一路讲到参数计算、抓包实测和调优抓手,中间穿插我自己踩过的坑。
1. 为什么信号满格还会卡:先看懂 CSMA/CA 要解决什么
1.1 一次典型的“人一多就卡”现场还原
先说一个我印象很深的场景。一个三十人左右的开放办公区,四台 AP 分布在吊顶上,2.4G 和 5G 双频都开着,单机 iperf 测速 5G 能到 400Mbps 以上,看起来一切正常。但只要上午十点大家同时上线开会,问题就来了:视频会议开始花屏、语音断续、文件同步卡在 99%。抓了一下终端侧的统计,tx retries在几分钟内涨到四位数,tx failed也有几十次,而信号强度清一色在 -60dBm 以上。
这就是典型的空口拥塞,而不是覆盖不足。无线介质只有一个,同一个信道上的所有终端和 AP 共享这一条“公共走廊”。当十几台设备同时有数据要发,它们必须靠一套规则来决定谁先说。规则设计得再巧妙,物理上也只有一条路,单位时间内能通过的帧数是有限的。CSMA/CA 就是这套规则,它保证了大家不会一直撞车,但它无法凭空创造容量。理解这一点非常关键:很多“网慢”的抱怨,本质是需求超过了空口的时间预算,而不是配置错了某个参数。
我在实际排查里形成了一个习惯,先算空口时间账,再看信号。具体做法是统计一分钟内该信道上所有帧的空中传输时长总和,如果占用率超过 60%,那么无论信号多好,用户体验都会明显下降。这个数字比任何 RSSI 截图都有说服力。
1.2 为什么无线不能像有线那样“边发边听”
以太网用的是 CSMA/CD,冲突检测。它的逻辑很朴素:一边发一边听,如果听到的信号和自己发的不一样,说明撞车了,立刻停手、退避、重来。这套机制在有线环境里成立,因为线缆里的信号衰减是可预测的,发送方发出的电平远高于其他站点窜进来的干扰,一比对就知道有没有冲突。
无线环境完全不是这样。首先,无线是半双工的,同一根天线在同一时刻要么发要么收,你发的时候根本听不见别人。其次,电磁波的动态范围极大,近处终端发来的信号可能比远处终端强几十 dB,当你正在发送时,远端那个微弱信号被本地强信号彻底淹没,硬件上检测不出来。第三,隐藏终端问题——A 和 B 都能连上 AP,但 A 和 B 之间互相听不见,各自以为信道空闲,同时向 AP 发送,在 AP 处撞个正着。这三条加起来,冲突检测在无线里根本没法可靠实现。
所以协议设计者换了个思路:既然冲突无法被及时检测,那就想办法尽量避免它发生,代价是引入额外的开销。这个思路转换,是理解后面所有机制的钥匙。冲突避免不是免费的,它要用帧间间隔、随机退避、确认帧这些“仪式感”换来确定性。
1.3 冲突避免的代价,以及它换来了什么
CSMA/CA 的核心交易是这样的:每次发送之前,站点必须监听信道;如果空闲,还要再等一段固定的帧间间隔;然后进入一个随机倒计时,倒到零才能发;发完之后必须等对方回一个确认帧,收不到就认为失败,重传并加倍退避窗口。这一套流程下来,一个 1500 字节的数据帧,真正用来传数据的时间可能只占总时间的六成左右,其余四成全是等待和握手。
听起来很不划算,但它换来了两样东西。第一是可预测性:虽然效率不高,但多站共存时不会出现持续性的全面碰撞崩溃,吞吐量随站点数增加是平滑下降而不是断崖式归零。第二是公平性:随机退避给了每个站点相同的机会,不会出现某个“嗓门大”的设备长期霸占信道。
我经常用会议室发言来类比:没有主持人时,一群人抢话,结果是谁也听不清;CSMA/CA 相当于规定了“说完一句必须停顿一下,让别人有机会插话,插话前先随机数几秒”。这个规则让会议效率降低了,但至少会议能开下去。你要做的不是取消规则,而是减少无意义的发言次数、把长发言合并成段,让每次开口都更值钱。
2. 协议骨架拆解:CSMA/CA 的四个核心动作
2.1 载波监听:物理监听和虚拟监听是两套并行机制
很多人以为“监听信道”就是测一下信号强度,实际上 802.11 里同时跑着两套监听。物理载波监听(CCA,空闲信道评估)由射频硬件完成,它看两件事:一是能量检测,收到的信号强度超过某个阈值(通常在 -82dBm 到 -62dBm 之间,随速率和带宽变化)就判定信道忙;二是前导码检测,能识别出符合 802.11 格式的前导序列,哪怕信号很弱也判定为忙,这个阈值更低、更灵敏。
虚拟载波监听则是协议层的,靠帧里的 Duration 字段实现。发送方在帧头里声明“我要占用信道多久”,所有听到这个帧的站点都会把这个时间写进自己的 NAV(网络分配矢量)计数器,在倒计时结束前一律认为信道忙,哪怕物理上听不到任何信号。这一手专门对付隐藏终端:A 听不到 B 的声音,但能听到 AP 转发的 CTS 帧,CTS 里带着时长,A 就被按住了。
两套机制是“或”的关系,任一方判定忙就不许发。这也解释了一个常见现象:明明环境里没有大流量,终端却一直退避。用抓包工具看 Duration 字段,有时候会看到异常巨大的值,说明有设备声明了超长占用时间,可能是某个异常终端或固件 bug,把整个 BSS 的 NAV 都拉满了。我遇到过一次,排查了半小时才发现是一台老设备的驱动问题,它发完数据后 Duration 没清零。
2.2 帧间间隔:用时间差划出的优先级车道
帧间间隔是这套协议里最精巧的设计。所有站点不是平等地等同一段时间,而是按帧的类型分配不同的固定等待时间,时间短的天然优先。这个设计把“优先级”直接编码进了时间轴,不需要额外的调度信令。
主要的三档是 SIFS、PIFS 和 DIFS。SIFS 最短,用于 ACK、CTS、分片后续帧这类必须立刻响应的场景,意思是我刚说完你必须马上接,中间不许别人插嘴。DIFS 最长,用于普通数据帧的初始接入。PIFS 介于两者之间,用于 AP 发送 Beacon 这类管理帧,让 AP 比普通终端稍微优先一点。
这里有个关键点需要注意:SIFS 是“优先级车道”的物理实现基础。因为 SIFS 比 DIFS 短,所以当一次数据交换开始后,接收方的 ACK 一定能在其他站点完成 DIFS 等待之前插进去。这就是所谓的“原子交换”,一次数据帧加一次 ACK 构成一个不可打断的整体。如果 SIFS 和 DIFS 的差值不够大,或者某个站点的定时器精度不够,这个原子性就会被破坏,出现 ACK 被别人抢了信道的情况,表现就是莫名重传。
2.3 随机退避:把“抢”变成“错峰”
光有帧间间隔还不够。如果两个站点同时监听到信道空闲,同时等完 DIFS,它们还是会同时发送。所以必须再引入随机性:每个站点在 0 到 CW(竞争窗口)之间随机取一个整数,乘以时隙长度,得到一个退避时间,在这个时间内每个时隙递减一次,减到零才能发送。
关键在于,退避计数器只在信道空闲时递减。一旦监听到忙,计数器立刻冻结,等信道重新空闲并再次等完 DIFS 后才继续递减。这个“冻结-恢复”机制保证了退避的公平性:已经等了很久的站点不会因为新来的站点而吃亏,它的进度被保留下来了。
竞争窗口 CW 是动态的。首次发送用 CWmin,如果发送失败(没收到 ACK),CW 翻倍,再失败再翻倍,一直翻到 CWmax 为止。这个机制叫二进制指数退避,它的作用是在拥塞时自动降低大家的发送欲望——撞得越狠,等待越久,给信道降温。反过来,每次成功发送后 CW 重置回 CWmin,让信道空闲时能快速响应。
我第一次深入看这块的时候有个疑问:CW 翻倍重试,那如果站点很多,退避时间岂不是越来越长,延迟爆炸?答案是肯定的,这正是设计意图。高密度场景下延迟本来就应该升高,因为物理上就是挤。真正该做的是减少同信道站点数,而不是指望协议变得更高效。
2.4 确认与重传:没有 ACK 就等于没发出去
无线链路的误码率比有线高好几个数量级,而且没有物理层的前向纠错兜底,所以必须有端到端的确认。每收到一个单播数据帧,接收方在 SIFS 之后回一个 ACK。发送方如果在超时时间内没收到 ACK,就认为这一帧丢了,进入重传流程。
重传不是简单重来,它会重新走一遍完整的接入流程:重新监听、重新等 DIFS、重新退避,只不过这次用的是翻倍后的 CW。这意味着每一次失败都会让该站点在接下来的竞争中更“谦让”,把机会让给别人。从全局看这是好事,从单个终端的体验看,就是延迟抖动。
重传次数是有上限的。短重传限制默认通常是 7 次,达到上限就丢弃该帧并上报给上层。上层如果是 TCP,会触发超时重传,延迟直接上到几百毫秒甚至秒级。这就是为什么在高密度环境下,你会看到 ping 值平时 3ms,偶尔蹦到 500ms——那一次多半是连续重传耗尽导致的。
注意:重传率高不等于信号差。信号差导致的失败通常集中在边缘终端;如果所有终端重传率都高,而信号都不错,那基本可以锁定同频干扰或者空口拥塞。
3. 参数计算:把竞争窗口、时隙和空口开销算明白
3.1 关键定时参数速查与差异对照
不同物理层标准的定时参数不一样,配错或者理解错会导致你的计算全盘跑偏。下面这张表是我实际算空口开销时常用的对照,单位都是微秒。
| 参数 | 802.11b (2.4G) | 802.11g (2.4G) | 802.11a (5G) | 802.11n/ac/ax (5G) |
|---|---|---|---|---|
| 时隙 Slot | 20 | 9 | 9 | 9 |
| SIFS | 10 | 10 | 16 | 16 |
| DIFS | 50 | 28 | 34 | 34 |
| CWmin | 31 | 15/31 | 15/31 | 15/31 |
| CWmax | 1023 | 1023 | 1023 | 1023 |
DIFS 的算法固定是 SIFS 加两个时隙。802.11g 是 10 + 18 = 28,802.11a 是 16 + 18 = 34,802.11b 是 10 + 40 = 50,对得上。CWmin 有两个值是因为早期标准里跳频和直扩的取值不同,现在基本统一走 15(对应 CW=31)或者 31(对应 CW=63),具体看厂商实现。
这里有个容易被忽略的细节:2.4G 上的 802.11n 用的是 802.11g 的定时参数,5G 上的 802.11n 用的是 802.11a 的。所以同样是 n 协议,插在不同频段上,帧间间隔是不一样的。做空口预算的时候如果混用,误差能到 20%。
3.2 一次成功发送到底要花多少微秒
拿 802.11g、54Mbps、单流、发一个 1500 字节的 UDP 负载来算。PSDU 长度 = 1500 + 8(LLC/SNAP)+ 24(MAC 头)+ 4(FCS)= 1536 字节,也就是 12288 bit。
- 物理层前导和头:ERP-OFDM 大约是 20µs
- 数据本体传输:12288 / 54 ≈ 227.6µs
- 数据帧合计:约 247.6µs
- SIFS:10µs
- ACK 帧(14 字节):20 + 112/54 ≈ 22.1µs
- DIFS:28µs
- 平均退避:CWmin/2 × Slot = 15/2 × 9 = 67.5µs
把成功路径的耗时加起来:247.6 + 10 + 22.1 + 28 + 67.5 ≈ 375.2µs。有效吞吐 = 12000 bit / 375.2µs ≈ 32Mbps。对比物理层 54Mbps,效率约 59%。
这个数字和实测是吻合的——单流 802.11g 的 TCP 吞吐通常就在 22 到 25Mbps,考虑到 TCP 握手和 ACK 的反向开销,59% 的物理层效率已经是比较理想的状态了。很多人在标称 54M 的链路上跑出 20M 就以为设备有问题,其实这就是协议的固有开销。
再看小包。同样是 802.11g,发一个 100 字节的语音包:PSDU = 136 字节,1097 bit,传输时间 20 + 20.3 = 40.3µs。总耗时 = 40.3 + 10 + 22.1 + 28 + 67.5 = 167.9µs。有效吞吐 = 800 bit / 167.9µs ≈ 4.8Mbps,效率只有 8.8%。
这个对比极其重要。1500 字节的帧效率 59%,100 字节的帧效率 8.8%,差了将近 7 倍。这就是为什么纯语音场景的容量远低于理论上限,也是为什么帧聚合技术(A-MPDU)会成为 802.11n 之后的核心优化——把多个小帧拼成一个大帧,一次竞争传完,把固定的接入开销摊薄。
3.3 竞争站点数对效率的杀伤力
上面的计算是“只有一个站点在发”的理想情况。真实环境里 n 个站点共享信道,每个站点在每个时隙有概率 τ 发送,冲突概率可以近似写成 p = 1 - (1 - τ)^(n-1)。
假设 10 个站点都有饱满的发送需求,稳态下 τ 大约在 0.05 上下,那么冲突概率约为 1 - 0.95^9 ≈ 37%。这意味着超过三分之一的时间片浪费在碰撞上。每次碰撞后 CW 翻倍,平均退避从 67.5µs 涨到 135µs,再涨到 270µs,空口时间被大量消耗在等待上。
更麻烦的是碰撞本身也是要花时间的。碰撞发生在数据帧传输过程中,双方会一直发完整个帧才发现失败。一个 1500 字节的帧碰撞一次,浪费的是 247µs 的空口时间,而这段时间本来可以给别的站点用。
我在一个二十人左右的场景里做过粗略统计:当在线活跃终端从 5 个增加到 15 个,同样业务的平均延迟从 8ms 增加到 45ms,重传率从 3% 涨到 18%。没有任何配置变动,纯粹是竞争加剧。这也解释了为什么高密度场景下,减小覆盖范围、增加 AP 数量、把终端分散到不同信道,效果远比调功率明显——你在减少每个碰撞域里的竞争者数量。
3.4 EDCA 四个接入类别是怎么抢的
802.11e 引入的 EDCA 把流量分成四个接入类别,每个类别有自己的 AIFSN(仲裁帧间间隔数)和 CW 参数。AIFS 的算法是 SIFS + AIFSN × Slot,AIFSN 越小优先级越高。
| 接入类别 | 典型业务 | AIFSN | CWmin | CWmax |
|---|---|---|---|---|
| VO(语音) | 通话、VoIP | 2 | 3 | 7 |
| VI(视频) | 视频流、会议 | 2 | 7 | 15 |
| BE(尽力而为) | 网页、文件 | 3 | 15 | 1023 |
| BK(背景) | 备份、下载 | 7 | 15 | 1023 |
语音的 AIFSN=2,AIFS = 16 + 18 = 34µs;背景的 AIFSN=7,AIFS = 16 + 63 = 79µs。语音比背景快了 45µs,而且 CWmin 只有 3,平均退避 13.5µs,背景是 67.5µs。两个因素叠加,语音抢到信道的概率远高于背景流量。
但这里有个实操中的陷阱:优先级只有在空口不拥塞的时候才有意义。当信道占用率超过 80%,即使是语音队列也要排队,因为硬件队列里堆满了,AIFS 再短也没用。而且如果 AP 侧没有正确配置 WMM,或者终端不认这套标记,优先级就是摆设。我见过不少“语音卡顿”的案例,最后发现是某款终端把语音包打成了 BE 标记。
提示:判断 WMM 是否真的生效,抓包看 QoS Control 字段的 TID 值。语音应该是 6,视频是 5 或 4,如果所有流量的 TID 都是 0,说明标记没打上。
4. 动手实录:抓一次完整的 DCF 交互
4.1 搭建监听环境
要看清楚 CSMA/CA 的真实行为,最直接的办法是把网卡切到监听模式抓空口包。Linux 下用一块支持监听模式的无线网卡就能做,命令不复杂但要按顺序来。
# 停掉接口,切换到监听模式 sudo ip link set wlan0 down sudo iw dev wlan0 set type monitor sudo ip link set wlan0 up # 锁定信道,注意带宽参数要和目标一致 sudo iw dev wlan0 set channel 6 20MHz # 开始抓包,限制快照长度避免文件过大 sudo tcpdump -i wlan0 -s 300 -w csma_capture.pcap几个实操要点值得说。第一,监听模式下网卡不会关联任何 AP,它只是被动接收空口上所有能解出来的帧,所以你必须手动锁定信道,否则网卡会跳来跳去,抓到的包是碎的。第二,快照长度设成 300 字节通常够用,MAC 头和大部分管理帧都能完整拿到,但如果你要分析帧聚合,得放到 1500 以上。第三,抓包位置很关键,网卡放在 AP 附近能听到绝大多数下行帧,但上行帧取决于终端的发射功率和距离,边缘终端的上行帧可能抓不全,分析重传时要考虑到这个偏差。
还有一个容易踩的坑:部分网卡驱动在监听模式下会丢掉物理层的部分信息,导致时间戳精度不够。如果你要精确算 SIFS 和 DIFS,建议用支持 radiotap 完整字段的芯片,抓出来的包在 Wireshark 里能看到每个帧的精确时间戳和信道状态。
4.2 Wireshark 过滤表达式与读包要点
抓到包之后,关键是过滤。空口上充斥着 Beacon、探测请求、广播,不筛一下根本看不清主线索。下面这几个过滤表达式我基本是条件反射地用:
wlan.fc.type_subtype == 0x08只看 Beacon,用来确认 SSID、支持的速率集、RSN 信息wlan.fc.type_subtype == 0x1d只看 ACK,观察确认帧的密度wlan.fc.type_subtype == 0x1b和0x1c看 RTS 和 CTS 对,判断是否启用了握手保护wlan.fc.retry == 1只看重传帧,这是判断链路质量最直接的指标wlan.fc.type_subtype == 0x28看 QoS 数据帧,配合wlan.qos.tid分析优先级
读包的时候我一般看三个东西。第一是帧的时间间隔,如果两个连续的数据帧之间只有十几微秒,那基本是同一个原子交换里的;如果间隔是几百微秒甚至毫秒,说明中间经历了完整的竞争流程。第二是 Duration 字段,NAV 的取值直接决定其他站点要等多久,异常大的值往往是问题的源头。第三是重传标志位,Wireshark 里会显示成“Retry”,连续出现的 Retry 帧说明某条链路在反复失败。
有一个判读技巧很实用:在 Wireshark 里打开“无线时间线”视图,把数据帧、ACK、重传按时间轴铺开,能一眼看出空口的忙闲分布。如果时间轴上大量空隙被 Beacon 和广播填满,说明管理帧开销过高;如果数据帧之间频繁出现 Retry,说明存在干扰或者覆盖问题。
4.3 用 iw 看终端侧的重传统计
抓包是上帝视角,但终端自己怎么想也很重要。Linux 下最方便的是iw命令,直接读驱动维护的统计。
iw dev wlan0 station dump输出里几个字段值得盯:
| 字段 | 含义 | 判读建议 |
|---|---|---|
tx retries | 重传帧计数 | 与总发送帧数对比,超过 10% 需要关注 |
tx failed | 达到重传上限被丢弃的帧数 | 任何非零值都说明有链路问题 |
signal | 最近一次收到的信号强度 | 低于 -70dBm 时速率会明显下降 |
tx bitrate | 当前发送速率 | 速率远低于预期说明信道质量差 |
expected throughput | 驱动估算的可用吞吐 | 与实测对比,偏差大说明有空口竞争 |
这些统计是累计值,看单次意义不大,要间隔采样看变化趋势。我的做法是写一个简单脚本,每 5 秒采一次,输出重传增量的变化,如果连续几个采样点重传增量都超过每秒几十次,就可以确认存在持续性问题。
这里有个细节需要注意:tx retries统计的是驱动层的重传次数,不包含硬件重试。有些网卡芯片在硬件层面会自行重发若干次,驱动根本感知不到。所以实际空口上的重传次数往往比这个数字更高,用抓包交叉验证会更准确。
4.4 一次实测数据的解读
我在一个干扰比较典型的环境里做过一次记录。信道 6 上有三个相邻的 BSS,终端距离自己的 AP 大约 8 米,中间隔一堵轻质墙。信号强度 -62dBm,协商速率 144Mbps(2.4G 双流)。
抓包统计一分钟的数据,几个关键数字是这样的:数据帧总数约 4200 个,其中重传帧约 780 个,重传率 18.6%;平均帧长 780 字节,说明有大量小包;RTS/CTS 帧全程为零,说明没开握手保护;ACK 总数略少于数据帧数,差的那部分正好是被丢弃的失败帧。
再看时间分布,信道占用率算下来接近 72%。这已经是很高的水平了,意味着有大量时间在传输和等待。重传率 18.6% 加上 72% 的占用率,基本可以断定是同频干扰叠加空口拥塞。进一步看干扰源,Beacon 帧里出现了三个不同的 BSSID,而且其中一个的信号强度和本 BSS 只差 10dB。
处理方式很直接:把 2.4G 上这三个 BSS 的手动信道错开,强制 1、6、11 中互不重叠的组合,把其中一台 AP 的功率从 20dBm 降到 14dBm 缩减覆盖,引导远端终端漫游到另一台。调整后同样业务的重传率降到 4.2%,占用率降到 41%,视频会议的卡顿投诉消失。整个过程没有换任何硬件,纯粹是在给 CSMA/CA 减少竞争者。
5. 调优实战:让 CSMA/CA 少犯错的几个抓手
5.1 干掉低速率,把空口时间省出来
这是投入产出比最高的一项调整,没有之一。很多 AP 出厂默认允许 1Mbps、2Mbps、5.5Mbps、11Mbps 这些基础速率接入,本意是保证远距离终端和极老旧设备能连上,代价是空口时间被大量浪费。
算一笔账:同样发 1536 字节的 PSDU,54Mbps 需要大约 248µs,1Mbps 需要多少?802.11b 用短前导 96µs,加 12288bit / 1Mbps = 12288µs,合计约 12.4ms。差了整整 50 倍。只要有一个终端还在用 1Mbps 发送,它一次发送就相当于别的终端发 50 次的空口时间。更要命的是,广播和多播帧必须用最低基础速率发送,Beacon 也是,所以基础速率越低,管理开销占用的空口越多。
具体怎么改?在 AP 配置里把基础速率集限制到不小于 12Mbps,同时把支持速率集的下限提到 6Mbps。以常见配置为例:
# hostapd 配置片段,速率单位是 500kbps # 120=6M, 240=12M, 480=24M, 540=27M basic_rates=120 240 480 540 supported_rates=120 180 240 360 480 540改完之后要注意两件事。一是老设备可能连不上了,如果场景里确实有只支持 802.11b 的终端,得先确认清楚再动手。二是覆盖边缘的终端可能因为速率不匹配直接掉线,这时候要靠功率调小或者补点来解决,而不是把速率放回去。我在实际场景里做过对比,把基础速率从 1Mbps 提到 12Mbps,同样的用户数下平均延迟下降约 35%,效果比换高增益天线明显得多。
注意:不要一刀切把基础速率设到 24M 以上。2.4G 频段穿墙损耗大,边缘终端用 24M 上行很容易失败,导致体验断崖式下降。12M 是个比较平衡的档位。
5.2 RTS/CTS 阈值怎么定
RTS/CTS 机制的作用是在发送大数据帧之前,先用两个很小的控制帧把信道“预约”下来,解决隐藏终端问题。但它是双刃剑:每个数据帧都加一对 RTS/CTS,等于把接入开销翻倍,低负载场景纯属浪费。
默认配置里 RTS 阈值通常设成 2347(大于最大帧长),等于关闭。什么时候该打开?我的判断标准是:如果抓包里能看到明显的碰撞,而且碰撞发生在帧传输的中段(说明发送方听不到对方),那就是隐藏终端,该开。或者场景里有大量长帧,碰撞一次损失很大,也值得开。
阈值设多少合适?一般设在 500 到 1000 字节之间。因为小帧本身传输时间短,碰撞损失小,不值得为它加握手;大帧碰撞损失大,用 RTS 保护更划算。
# 开启 RTS 保护,阈值 1000 字节 rts_threshold=1000实测下来,在存在隐藏终端的环境里,开启 RTS 能让重传率从 20% 以上降到个位数,代价是整体吞吐下降约 5% 到 10%。这是一个用吞吐换稳定性的典型取舍,视频会议这类对抖动敏感的业务感知会明显改善,纯下载业务则会略微变慢。
5.3 信道与功率规划的基本盘
同频干扰是 CSMA/CA 效率的头号杀手,因为它让所有站点都在为同一段空口时间竞争。2.4G 只有三个真正互不重叠的信道(1、6、11),在办公室里往往被周围的 AP 占满,这时候任何“优化”都是在夹缝里求生存。
有效的做法是主动降功率。很多人觉得功率越大覆盖越好,实际上大功率带来的问题更多:一是扩大了同频干扰的范围,你的 AP 和邻家的 AP 在同一信道互相压制;二是终端会“粘”在远端 AP 上不肯漫游,形成远距离低速率连接,白白消耗空口时间。把 2.4G 功率控制在 14 到 17dBm,5G 控制在 17 到 20dBm,配合合理的 AP 密度,效果通常比满功率好得多。
5G 频段的选择空间大得多,但也有个坑:开了 80MHz 甚至 160MHz 带宽之后,可用信道数量急剧减少,在密集部署场景里反而更容易撞车。我的经验是,高密度办公场景用 40MHz,家庭或小规模用 80MHz,只有极少数对单流带宽有强需求的场景才上 160MHz。带宽翻倍带来的速率提升,往往被同频干扰抵消掉还倒亏。
5.4 帧聚合与 Block ACK:对抗固定开销的正解
前面算过,小包的协议效率只有 8% 左右,根源在于每次发送都要付一遍 DIFS、退避、SIFS、ACK 的固定成本。802.11n 引入的 A-MPDU 和 Block ACK 就是冲着这个来的:把多个数据帧拼成一个聚合帧一次发出,接收方用一个 Block ACK 统一确认,把 N 次竞争压缩成 1 次。
效果有多明显?假设把 10 个 100 字节的帧聚合成一个 1000 字节的聚合帧,原本需要 10 次完整接入,现在只需要 1 次。固定开销从 10 份降到 1 份,效率能从 8% 提到 50% 左右。这也是为什么 802.11n 之后,标称速率和实际吞吐的比例反而变好了。
但聚合不是无脑开就好。聚合帧越长,占用空口的时间越久,其他站点的延迟就越大;而且聚合帧一旦出错重传代价也更高。所以在语音和游戏这类低延迟业务场景里,过度聚合反而有害。判断标准是看业务的延迟敏感度:批量下载可以放心聚合,实时交互要控制聚合长度。
Block ACK 还有个细节要注意,它是有会话窗口的,接收方要维护重排序缓冲区。如果窗口设置太小,聚合效果打折;太大则占用终端内存。一般让设备自动协商就行,除非你在做嵌入式开发需要手工调参。
5.5 省电模式带来的延迟抖动
这块很容易被忽略,但实际影响不小。终端为了省电,会进入省电模式,告诉 AP“我睡着了,有我的包先缓存着”。AP 收到这个标记后,会把发往该终端的帧缓存起来,等到 Beacon 周期(默认 100ms)的 TIM 指示位通知后才发送。
问题在于,这个缓存-唤醒的周期会引入最长 100ms 量级的延迟。对网页浏览没感觉,对语音通话就是明显的抖动。更麻烦的是有些终端即便在活跃使用中也会频繁进出省电状态,导致延迟时高时低,很难复现。
排查方法是在抓包里看终端的帧里有没有 Power Management 位被置位。如果有,而且业务是延迟敏感的,可以考虑在 AP 上关闭部分省电优化,或者调整 Beacon 间隔。把 Beacon 间隔从 100ms 降到 50ms 能缩短唤醒等待,代价是管理帧开销翻倍,空口占用增加。这是个取舍,得根据实际业务的敏感度来定。
提示:Beacon 间隔不建议低于 50ms,也不建议高于 200ms。低于 50ms 管理开销太大,高于 200ms 会导致漫游和唤醒响应变慢,终端容易掉线。
6. 常见问题与排查速查表
6.1 典型现象与定位路径
排查 CSMA/CA 相关的问题,最怕的是没有方向乱试。我把这些年遇到的典型情况整理成一张表,基本上照着现象走就能缩小范围。
| 现象 | 优先怀疑 | 快速验证手段 | 处理方向 |
|---|---|---|---|
| 单机快、并发慢 | 空口拥塞 | 算信道占用率,看活跃终端数 | 分流信道、降功率、加 AP |
| 重传率高但信号好 | 同频干扰或隐藏终端 | 抓包看碰撞位置和 BSS 数量 | 错开信道、开 RTS/CTS |
| 延迟周期性抖动 | 省电模式或 Beacon 周期 | 抓 PM 位,看抖动周期是否接近 100ms | 调 Beacon 间隔、关省电 |
| 边缘终端极慢 | 低速率连接 | 看协商速率和 MCS | 降功率、加覆盖点、限制基础速率 |
| 小包业务吞吐低 | 固定开销占比高 | 按帧长算理论效率 | 开聚合、减少小包 |
| 漫游时断流 | 竞争过程中的重关联 | 抓包看重关联前后的帧序列 | 优化漫游门限、开快速漫游 |
| 某类业务被压制 | EDCA 优先级没生效 | 看 QoS Control 的 TID | 检查 WMM 配置和终端标记 |
用这张表的时候有个原则:先确认问题是在空口层还是在网络层。最简单的判断方法是看同信道的其他终端是否也慢。如果只有一台终端慢,那多半是该终端的链路问题;如果一片都慢,那就是空口的问题,往上走拥塞和干扰的方向查。
6.2 容易被忽略的几个坑
第一个坑是把信号强度和链路质量划等号。RSSI 只反映接收功率,不反映信噪比。在干扰环境下,信号 -55dBm 但噪声底也高到 -75dBm,信噪比只有 20dB,实际能用的 MCS 可能只有 3 或 4。所以看统计一定要同时看 signal 和 noise,iw的输出里这两个字段都有,别只看一个。
第二个坑是忽视了上行和下行的不对称。AP 的发射功率通常远大于终端,所以下行覆盖看起来很好,但终端上行到 AP 的信号可能早就衰减到边缘了。表现就是下载很快、上传很慢,或者终端明明显示信号好却老是重传。这种情况要靠调整 AP 功率让上下行平衡,而不是一味加功率。
第三个坑是在高密度场景里迷信“更高协议”。很多人以为换成 Wi-Fi 6 的 AP 就能解决问题,实际上如果信道规划没做好、终端还是老设备、基础速率没动,换设备带来的改善非常有限。协议升级带来的是更高的物理层效率和更好的多用户调度能力,但它不会改变“同信道共享一条介质”这个物理事实。
第四个坑是忽略非 Wi-Fi 干扰源。2.4G 频段上和蓝牙、无线键鼠、微波炉共用,这些干扰在频谱上看是一片噪声,不会被协议识别为“忙”,而是直接导致误码。表现就是重传率高但抓包看不到碰撞帧,因为帧在物理层就被打坏了,压根没解出来。这种要靠频谱分析工具定位,光抓包看不出来。
第五个坑是在虚拟化或容器环境里做无线测试。虚拟网卡的定时精度和真实硬件差得远,用它测出来的 CSMA/CA 时间参数全是错的,只适合做功能验证,性能数据一概不能信。
6.3 一些不那么“标准”的经验
最后分享几条文档里不会写的东西。第一条是关于退避计数器的:有些廉价网卡在信道忙时不会真正冻结退避计数器,而是直接清零重新开始,这会导致该终端在竞争中永远吃亏,表现为同一位置的终端速率差异巨大。换成正规网卡就好了,但这种问题很难从配置上发现。
第二条是关于漫游的。终端漫游本质上是一次重新竞争加重新认证,如果漫游门限设得太松,终端会在两个 AP 之间反复横跳,每次都要重新走一遍接入流程,空口时间全浪费在管理帧上。宁可让终端粘得久一点,也不要频繁漫游。
第三条,我在做物联网设备接入的时候发现,低功耗终端的 CSMA/CA 实现往往不完整,有些模块为了省电直接跳过载波监听,抓到空口就发。这类设备数量一多,整个 BSS 的碰撞率会飙升,而且抓包看起来像是“无缘无故的碰撞”。遇到这种情况,最有效的办法是给这些设备单独划一个 SSID 绑到独立信道,把它们和主业务隔离开。
如果你现在手上正好有一个“信号满格但就是卡”的场景,我建议的动手顺序是:先抓一分钟包算信道占用率和重传率,再看活跃终端数和 BSS 数量,然后调基础速率和发射功率,最后才考虑 RTS 阈值和聚合参数。这个顺序是我踩了不少坑之后总结出来的,按它走能省很多瞎试的时间。