2、BLE协议栈基础:物理层、链路层、L2CAP、ATT/GATT 层次结构

做BLE吞吐测试,你得先搞清楚协议栈长什么样。

很多人一上来就调参数、改MTU,结果吞吐死活上不去。为什么?因为他不理解每一层在干什么。我刚开始做蓝牙开发时也犯过这毛病,后来被一个老工程师点醒——「你不懂协议栈,你调什么参数?」

好,今天我们就一层层剥开BLE协议栈。从最底下的物理层,一直看到最上面的应用层。

核心观点:BLE吞吐的瓶颈,往往不在某一层,而在层与层之间的配合。你只有看懂每一层的职责,才知道该从哪里下手优化。

2.1 整体架构:四层模型

BLE协议栈分四层,从下往上分别是:

  • 物理层(PHY)—— 负责发比特流,管频率、调制、速率
  • 链路层(LL)—— 负责连接管理、重传、加密
  • L2CAP —— 负责数据分包重组、通道复用
  • ATT/GATT —— 负责数据读写、服务发现

说白了,数据从应用层往下走,每层加点头部信息,到物理层变成无线电波飞出去。对面收到后,再一层层剥开,拿到原始数据。

我画了张图,帮你直观理解这个结构:

BLE 协议栈四层结构 应用层 (App / Profile) ATT / GATT L2CAP 链路层 (Link Layer) 物理层 (PHY) 用户数据入口 服务定义 属性读写 服务发现 数据分包 通道复用 连接管理 重传/加密 调制解调 1M/2M/LR 数据流向:应用层 → 物理层(发送方向) 物理层 → 应用层(接收方向) 吞吐 关键层

我的经验:吞吐测试时,我习惯先看L2CAP和链路层。这两层是数据包大小的主要限制点。物理层速率再高,如果L2CAP分包不合理,吞吐照样上不去。

2.2 物理层(PHY)—— 最底层的速度基础

物理层负责把比特变成无线电波。BLE在2.4GHz频段工作,和Wi-Fi、Zigbee挤在一起。

BLE物理层有三种模式:

PHY模式 原始速率 有效吞吐(估算) 适用场景
1M PHY 1 Mbps ~700 kbps 兼容性最好,默认模式
2M PHY 2 Mbps ~1.4 Mbps 高吞吐场景,BLE 5.0引入
LR PHY (长距离) 125/500 kbps ~80-350 kbps 远距离、低速率

你想想看,2M PHY的原始速率是1M的两倍,但实际吞吐能翻倍吗?不一定。因为协议开销还在那里。

注意:我曾经在一个项目中,把PHY从1M切换到2M,结果吞吐只提升了40%。为什么?因为链路层的连接间隔没调,L2CAP的MTU也没改。物理层只是管道,管道粗了,但水龙头没拧大,水流还是上不去。

2.3 链路层(LL)—— 连接的心脏

链路层是BLE协议栈里最复杂的一层。它管三件事:

  • 连接管理—— 建立连接、断开连接、调整连接参数
  • 数据重传—— 丢包了自动重发,保证可靠性
  • 加密—— AES-128加密,保证数据安全

链路层的工作方式是「事件驱动」。主设备和从设备约定好时间点,到了时间就交换数据。这个时间点叫连接事件(Connection Event)。

连接事件有两个关键参数:

参数 含义 对吞吐的影响
Connection Interval 两个连接事件之间的间隔 间隔越小,吞吐越高,但功耗也高
Slave Latency 从设备可以跳过的事件数 跳过的越多,吞吐越低,但省电

举个例子。连接间隔设成7.5ms(最小值),每个事件能发4个包(LL Data Length Extension支持)。那么一秒钟大概能发:

1000ms / 7.5ms ≈ 133 个事件
每个事件 4 个包
总共 ≈ 532 个包/秒

如果每个包能带251字节数据,那理论吞吐就是:

532 × 251 × 8 ≈ 1.07 Mbps

嗯,这里要注意。这只是链路层的理论值。到了L2CAP和ATT层,还要再打折扣。

避坑指南:我曾经调吞吐时,把连接间隔设到最小值,结果设备频繁断连。后来发现是射频环境太差,重传太多,链路层忙不过来了。所以连接间隔不是越小越好,得看实际环境。

2.4 L2CAP —— 数据的分包与重组

L2CAP在链路层之上,负责两件事:

  • 数据分包—— 把上层的大数据包切成链路层能处理的小包
  • 通道复用—— 多个应用可以共用同一个连接

L2CAP有个重要概念叫MTU(Maximum Transmission Unit)。它决定了上层一次能发多少数据。

BLE 4.2之前,L2CAP的MTU默认是23字节。你没看错,23字节。去掉3字节的ATT头部,实际有效数据只有20字节。这就是为什么老版本的BLE吞吐那么低。

BLE 4.2引入了LE Data Length Extension,把链路层的数据包从27字节扩展到了251字节。L2CAP的MTU也跟着涨到了247字节左右。

到了BLE 5.0,又引入了LE 2M PHY,速率翻倍。但L2CAP的MTU没变,还是247字节。

BLE版本 链路层包大小 L2CAP MTU 有效数据(ATT层)
4.0 / 4.1 27 字节 23 字节 20 字节
4.2 251 字节 247 字节 244 字节
5.0+ 251 字节 247 字节 244 字节

关键点:做吞吐测试时,第一件事就是确认L2CAP的MTU。如果MTU只有23字节,你就算用2M PHY,吞吐也上不去。我见过太多人在这上面踩坑了。

2.5 ATT/GATT —— 应用层的读写接口

ATT(Attribute Protocol)定义了数据的读写方式。GATT(Generic Attribute Profile)定义了数据的组织方式。

说白了,ATT是「怎么读写」,GATT是「读写什么」。

ATT有6种操作:

  • Request / Response —— 一问一答,可靠但慢
  • Command —— 只发不问,快但不可靠
  • Notification —— 从设备主动推数据,不需要确认
  • Indication / Confirm —— 从设备主动推数据,需要确认

吞吐测试时,我们一般用Notification。为什么?因为它不需要确认,可以连续发。Indication要等确认,效率低一半。

GATT把数据组织成Service和Characteristic。一个Service包含多个Characteristic,每个Characteristic有一个Value。我们读写的就是这个Value。

我的习惯:做吞吐测试时,我会专门定义一个「吞吐测试Service」,里面放一个「大块数据Characteristic」。然后用Notification往里面灌数据。这样能排除其他服务的干扰,只看纯吞吐。

2.6 各层对吞吐的影响总结

好,我们把四层串起来,看看每一层对吞吐的影响:

层次 影响吞吐的关键因素 优化方向
物理层 PHY模式(1M/2M/LR) 切换到2M PHY
链路层 连接间隔、Data Length 缩小连接间隔,启用DLE
L2CAP MTU大小 协商MTU到最大值
ATT/GATT 操作类型(Notification vs Indication) 使用Notification,避免等待确认

你看,每一层都有瓶颈。只优化一层,效果有限。必须四层一起调,才能把吞吐拉满。

再次提醒:我曾经帮一个客户调吞吐,他坚持说「我PHY已经是2M了,为什么吞吐还是上不去?」我一看代码,L2CAP的MTU还是23字节。这就是典型的「只知其一,不知其二」。协议栈是整体,不是单点。