BLE L2CAP 解析

概念

L2CAP 在ATT层下,HCI层上,主要作用是将上层协议的数据传输给下层,把下层收到的数据传输给上层

L2CAP功能

  • 协议/信道复用 (Protocol/Channel Multiplexing) : L2CAP 允许在单个物理连接上同时支持多个逻辑连接。它通过使用信道标识符(CID)来区分不同的数据流,并将它们路由到相应的上层协议。这种复用能力使得多个应用程序可以同时通过一个蓝牙链路进行通信
  • 分段与重组 (Segmentation and Reassembly): L2CAP 能够将上层协议发送的大数据包(服务数据单元,SDU)分割成较小的数据包,以便在底层的基带上传输。在接收端,L2CAP 会将这些分段后的数据包重新组装成原始的数据包。这项功能使得蓝牙可以传输高达64KB的大型数据包。
  • 流量控制 (Flow Control) : L2CAP 支持按通道进行流量控制,确保发送方不会发送超出接收方处理能力的数据量。特别是在低功耗蓝牙(BLE)中,采用了基于信用的流量控制模式(Credit Based Flow Control Mode)。
  • 错误控制和重传 (Error Control and Retransmissions) : L2CAP 提供了错误检测和重传机制,以确保数据的可靠传输。
  • 服务质量 (QoS) 协商: 在建立 L2CAP 连接时,设备可以交换有关预期服务质量的信息,以确保满足不同应用的延迟和带宽要求。

L2CAP数据包格式

基础 PDU

L2CAP层PDU基本结构如下

scss 复制代码
<----------------------- L2CAP PDU (B-Frame) ----------------------->
+-------------------------+-----------------------------------------+
|     L2CAP Header (4)    |      Information Payload (可变长度)     |
+------------+------------+-----------------------------------------+
| Length (2) |  CID (2)   |           (ATT PDU 或 SMP PDU)          |
+------------+------------+-----------------------------------------+

可以看到分三部分

字段名 长度 含义
Length 2byte 后续Payload部分的总长度,不包括 Length 和 Channel ID 字段本身
CID 2byte 逻辑通道标识符
Payload 可变长度 上层协议数据,如ATT、SMP等,包含了来自上层协议的完整 PDU

常见CID如下

CID值 协议/服务
0x0004 ATT(属性协议,用于GATT通信)
0x0005 L2CAP信令通道
0x0006 SMP(安全管理协议)
0x0040~0x007E 用户自定义通道

这就是L2CAP层的基础PDU格式,也就是我们所说的Bframe

信令 PDU

信令PDU也就是指CID为0x0005的基础PDU,专门用于 L2CAP 协议自身的管理和控制,它不承载任何用户应用数据。

作用

  • 建立连接 (Connection Request/Response)
  • 配置通道参数 (Configuration Request/Response)
  • 断开连接 (Disconnection Request/Response)
  • 流量控制信用更新 (LE Credit Based Connection)

结构

信令PDU结构如下

scss 复制代码
+----------------------+------------------------------------------------------------+
|   L2CAP Header(2B)   |               Information Payload(可变长度)                 |
+------------+---------+----------+----------------+------------+-------------------+
| Length(2B) | CID(2B) | Code(1B) | Identifier(1B) | Length(1B) | Command Data(N B) |
+------------+---------+----------+----------------+------------+-------------------+

PayLoad结构如下:

字段名 长度 含义
Code 1 byte 信令命令Code
Identifier 1 byte 用于匹配请求和响应。当客户端发送一个请求时,会附带一个唯一的 Identifier。服务器在响应时,会使用相同的 Identifier,这样客户端就知道这个响应是对应哪个请求的
Length 2 byte 后面data字段的长度
Command Data 可变 包含了信令命令所需要的具体参数

信令通道PDU格式,也被成为Cframe

信令命令

Code (Hex) 命令名称 (Command Name) 分类 描述
0x01 Command Reject 通用 拒绝一个无效的信令命令。其原因码 (Reason) 有 BLE 特定的定义。
0x06 Disconnection Request 通用 请求关闭一个 LE L2CAP 通道。
0x07 Disconnection Response 通用 Disconnection Request 的响应。
0x12 Connection Parameter Update Request 链路层参数更新 由从机(Peripheral)发起,请求主机(Central)更改连接参数,如连接间隔、从机延迟等。
0x13 Connection Parameter Update Response 链路层参数更新 主机对 0x12 请求的响应,告知从机是否接受新的连接参数。
0x14 LE Credit Based Connection Request LE CoC 核心命令。请求建立一个支持信用流控的 LE L2CAP 通道,并协商 MTU, MPS 和初始信用。
0x15 LE Credit Based Connection Response LE CoC 0x14 的响应。返回 MTU, MPS, 初始信用和连接结果。
0x16 LE Flow Control Credit LE CoC 核心流控命令。用于向对方设备补充数据包发送信用(Credits),是实现 LE 流量控制的关键。

COC PDU

COC(Connection-Oriented Channel)是指面向连接通道,主要作用是为BLE建立一条带流控的、可传输大量数据的通道

PDU格式

scss 复制代码
<-----------------------         L2CAP PDU     ---------------------------->
+------------+-----------+--------------+----------------------------------+
| Length (2) |  CID (2)  | SDU Length(2)|       Information Payload        |
+------------+-----------+--------------+----------------------------------+
仅首帧携带SDU长度,后续没有

可以看到COC PDU格式与基本PDU格式相同,只是首帧携带了SDU的长度,标识了本次数据传输要传输的总字节数

三种PDU比较

官方概念 Basic Mode PDU Signaling PDU CoC Data PDU
作用 传输 GATT/SMP 等基础数据 传输 L2CAP 控制指令 传输流式应用数据
所属通道 固定通道 (ATT, SMP) 信令通道 (CID 0x0005) 动态通道 (CoC)
核心机制 简单复用 命令与响应 基于信用的流量控制 (Credit-Based Flow Control)
命名来源 Basic (基础) Control (控制) Kredit / Credit (信用)

L2CAP工作模式

  • 基本模式 (Basic Mode): 这是最常见的工作模式,用于ATT和SMP等固定通道。它提供了一个简单的、不可靠的数据报服务。在这种模式下,L2CAP主要负责协议复用以及基本的分段和重组,但不提供流量控制或错误重传(这些由链路层负责)。我们日常的GATT读写操作就是运行在这种模式之上。
  • 基于信用的流量控制模式 (LE Credit Based Flow Control Mode): 这是为L2CAP面向连接通道(CoC)设计的先进模式。它提供了一种可靠的、面向连接的通道服务。在这种模式下,数据传输基于"信用(Credit)"系统:接收方会授予发送方一定数量的信用,发送方每发送一个数据包就消耗一个信用,直到信用耗尽。只有当接收方处理完数据并授予新的信用后,发送方才能继续发送。这种机制实现了端到端的流量控制,确保了数据传输的可靠性和高效性,非常类似于TCP协议中的滑动窗口。

面向连接通道COC

作用

传统的GATT模型是基于请求-响应模式的,并且受到ATT_MTU大小的限制。对于需要传输大量数据流的场景(如音频、批量传感器数据),GATT的效率并不高。每次传输都需要一次请求和一次响应/通知,这带来了额外的延迟和协议开销。

而L2CAP CoC则在两个设备之间建立了一条类似TCP Socket的直接数据管道。数据可以在这条管道上自由流动,无需等待请求-响应,并且拥有自己的流量控制,从而实现了高吞吐量和低延迟。

工作原理

1、一个设备发起连接请求,指定一个上层协议的标识(PSM - Protocol/Service Multiplexer)。

PSM(Protocol/Service Multiplexer)是 L2CAP 用来区分不同逻辑通道(service)的一段 16 位标识,它让单一物理链接(BR/EDR 或 LE)上可以并行承载多个应用协议。相当于TCP协议中的端口号

2、另一个设备监听这个PSM,并接受连接请求。

3、双方通过L2CAP信令通道交换连接参数(如MTU、MPS和初始信用)。

4、连接建立后,双方获得一个动态分配的CID,数据就可以在这个新的通道上基于信用流模式进行传输。

BLE建立连接和交换数据的过程

1.物理链路建立与 L2CAP 信令通道就绪

主机通过 HCI 下发 Connect 命令,Controller 完成 Link Layer 建连后上报 LE Connection Complete 事件

在这个阶段,L2CAP 层还没有开始主动交换数据。它只是被告知"连接已建立"。此时,L2CAP 内部的三个重要通道已经准备就绪:

  • ATT 通道 (CID=0x0004),该通道传输基础PDU
  • 信令通道 (CID=0x0005),该通道传输信令PDU
  • SMP 通道 (CID=0x0006),该通道传输基础PDU

2.MTU交换

ATT 层发起 Exchange MTU Request/Response(Opcode=0x02/0x03),携带本端 MTU 值。

在 L2CAP 层,使用基础PDU承载命令,在ATT通道传输

scss 复制代码
┌───────────────┬───────────┬──────────────────────────┐
│ Length (2B)   │ CID (2B)  │ ATT_PDU (Opcode + MTU)   │
│ =3            │ =0x0004   │ =1B+2B                   │
└───────────────┴───────────┴──────────────────────────┘
​

交互PDU:

  • 客户端 → 服务器:Exchange MTU Request (Opcode=0x02, MTU_Value)
  • 服务器 → 客户端:Exchange MTU Response (Opcode=0x03, MTU_Value)

3.连接参数更新

连接参数更新使用信令PDU,在信令PDU传输

如:

scss 复制代码
+------------------------------------------+-----------------------------------------------------------------------------+
|             L2CAP Header(2B)             |                              Signaling Header                               |
+-----------------+------------------------+--------------------------------------+-----------+--------------------------+
|Length(2B) 0x000C| Channel ID(2B) 0x0005  |Code(1B) 0x12 ConnParam Update Request|  ID(1B)   |    CmdLen(2B) 0x0008     |
+------------------------------------------------------------------------------------------------------------------------+
|                                                      PayLoad(8B)                                                       |
+-----------------------+------------------------+-----------------------------------------+-----------------------------+
|Interval Min(2B) 0x000A|Interval Max(2B) 0x000A |        Slave Latency(2B) 0x0000         |Timeout Multiplier(2B) 0x000F|
+-----------------------+------------------------+-----------------------------------------+-----------------------------+
​

服务端收到参数更新请求后,会回复Code=0x13 ConnParam Update Response

4.ATT/GATT数据交换

所有 ATT/GATT 操作(读、写、Notify、Indicate)均以基础PDU承载,在ATT通道上传输

PDU结构

ini 复制代码
┌───────────────┬───────────┬───────────────────────┐
│ Length (2B)   │ CID=0x0004│ ATT_Payload (N B)     │
└───────────────┴───────────┴───────────────────────┘
​

5.(扩展)动态通道

5.1 发起动态通道建立请求

使用信令PDU,在信令通道上发起建立动态通道的请求

scss 复制代码
+---------------------------------------------------------------+----------------------------------------------+
|                       Length(2B) 0x000E                       |            Channel ID(2B) 0x0005             |
+------------------------------------------------+-----------------------------+-------------------------------+
|Code(1B) 0x14 LE Credit Based Connection Request|       Identifier(1B)        |       CmdLen(2B) 0x000A       |
+-----------------+------------------------------------+-----------------+-----------------+-------------------+
|     PSM(2B)     |              SCID(2B)              |     MTU(2B)     |     MPS(2B)     |Initial Credits(2B)|
+-----------------+------------------------------------+-----------------+-----------------+-------------------+
​

CommandData中:

PSM是区分逻辑通道的标识,上面有介绍

SCID是发起方为这个链接分配的接受CID,后续数据交互都要使用这个CID

MTU是发起方能接受的SDU的最大尺寸

MPS是Maximum PDU payload Size,定义了L2CAP层单个数据包(PDU)所能承载的最大数据量

Credits是信用值,实现流控的关键,Initial_Credits表示L2CAP层给这个通道准备了多少个接受缓冲区

5.2 收到回复

使用信令PDU,在信令通道上传输

payload如下:

scss 复制代码
+----------+---------+---------+---------------------+------------+
| DCID(2B) | MTU(2B) | MPS(2B) | Initail Credits(2B) | Result(2B) |
+----------+---------+---------+---------------------+------------+

DCID是服务器给自己的连接分配的ID,CoC通道就是由这一对(SCID、DCID)CID组成的

Result是连接结果,0x0000表示成功,其他值为各种错误原因

5.3 数据传输

使用COC PDU,在COC通道上传输

结构:

首帧:

scss 复制代码
+----------------+--------------+-----------------+--------------------------+
| L2CAP Len (2B) | Dest.CID(2B) | SDU Length (2B) | Payload (第一个数据分片)    |
+----------------+--------------+-----------------+--------------------------+
Dest.CID: 目标设备的接收 CID(比如手机发送时,填 `0x0042`)。
SDU Length: 整个应用层数据块 (SDU)的总长度。这个字段只在首帧出现,告诉接收方需要准备多大的重组缓冲区。

后续帧:

scss 复制代码
+----------------+--------------+--------------------------+
| L2CAP Len (2B) | Dest.CID(2B) | Payload (第一个数据分片)    |
+----------------+--------------+--------------------------+

只有首帧有SDU Length,每发送一帧消耗一个Credit,直到消耗尽为止

5.4 信用补给

使用信令PDU,在信令通道传输

接收方给发送发补充信用值,发送Flow Control Credit Command

css 复制代码
------ L2CAP Header ------
┌───────────────┬───────────────────────────┐
│ Length (2 B)  │ Channel ID (2 B)          │
│ =0x0007       │ =0x0005 (LE Signaling)    │
└───────────────┴───────────────────────────┘
------ LE Signaling Header ------
┌──────────────┬───────────────────────────┐
│ Code (1 B)   │ Identifier (1 B)          │
│ =0x16        │ =Z                        │
├──────────────┴───────────────────────────┤
│ Command Length (2 B)                     │
│ =0x0003                                  │
└──────────────────────────────────---------------------─┘
------ Command Data ------
┌───────────────┬───────────────────────────┐
│ Dest_CID (2 B)│ Credits_Added (1 B)       │
│ =0xXXXX       │ =N                        │
└───────────────┴───────────────────────────┘
相关推荐
杨杨杨大侠1 分钟前
第4篇:AOP切面编程 - 无侵入式日志拦截
java·后端·开源
IT_陈寒2 小时前
Python 3.12 新特性实战:5个让你的代码效率提升50%的技巧!🔥
前端·人工智能·后端
Apifox2 小时前
Apifox 8 月更新|新增测试用例、支持自定义请求示例代码、提升导入/导出 OpenAPI/Swagger 数据的兼容性
前端·后端·测试
风飘百里2 小时前
Go语言DDD架构的务实之路
后端·架构
郭庆汝2 小时前
GraphRAG——v0.3.5版本
后端·python·flask
轻松Ai享生活2 小时前
Linux Swap 详解 (2) - 配置与优化
后端
xiguolangzi2 小时前
springBoot3 生成订单号
后端
用户6757049885022 小时前
从入门到实战:一文掌握微服务监控系统 Prometheus + Grafana
后端