Core_v6.2 Vol 3 Part F 学习笔记(二):Protocol Overview——ATT 协议模型

0. 本篇说明

本文继续学习 Bluetooth Core Specification v6.2:

Vol 3, Part F:Attribute Protocol,简称 ATT

本篇对应章节:

2 Protocol overview

上一篇我们已经知道,ATT 是用来发现、读取、写入对端设备 attributes 的协议。
这一篇开始进入 ATT 的协议模型,也就是搞清楚:

  • ATT 有哪些角色;

  • Server 是什么;

  • Client 是什么;

  • Attribute 到底由哪些东西组成;

  • Attribute type、handle、permissions 分别干什么;

  • 为什么一个设备可以同时是 Client 和 Server;

  • 为什么一个设备上只能有一个 ATT Server 实例;

  • ATT bearer 是什么;

  • LE 和 BR/EDR 上 ATT bearer 有什么差异。

一句话概括本篇:

ATT 是一个 Client/Server 模型的属性访问协议,Server 暴露 Attribute Database,Client 通过 ATT bearer 访问这些 attributes。

更接地气一点:

Server 像“货架管理员”,货架上摆着一堆带编号的商品;
Client 像“顾客”,通过商品编号去查、读、写;
ATT bearer 像“柜台窗口”,请求和响应都从这个窗口走。


1. Protocol Overview 原文结构拆解

这一节虽然标题叫 “Protocol overview”,但它其实浓缩了 ATT 后面所有章节的核心思想。

可以拆成 8 个知识点:

编号 知识点 说明
1 ATT 定义两种角色 Server role 和 Client role
2 Server 暴露 attributes Client 通过 ATT 访问这些 attributes
3 Attribute 有三个关联属性 type、handle、permissions
4 Attribute type 说明属性代表什么 通常由 UUID 表示
5 Attribute handle 唯一标识 attribute Client 靠 handle 读、写、接收通知
6 Permissions 用于限制访问 但不能通过 ATT 直接访问 permissions
7 设备可以同时是 Client 和 Server 同一设备可以双角色并发
8 ATT 请求通过 ATT bearer 发送 一个 bearer 对应一个 L2CAP channel

后面所有 PDU,本质上都围绕这几个点展开。


2. ATT 定义两种角色:Server 和 Client

2.1 原文含义

Protocol overview 开头说明:

Attribute Protocol 定义两种角色:server role 和 client role。

这里要注意,ATT 的 Client/Server 不是物理设备类型,也不是 Central/Peripheral 的同义词。

2.2 逐行解释

原文含义 中文解释 工程理解
ATT defines two roles ATT 定义两种角色 这是 ATT 自己的逻辑角色
a server role Server 角色 保存 attributes,响应访问
a client role Client 角色 访问对端 attributes
server exposes attributes Server 暴露一组 attributes Server 有 Attribute Database
client accesses attributes Client 通过 ATT 访问这些 attributes Client 发 request / command

2.3 Server 是什么?

ATT Server 是保存 attributes 的一方。

ATT Server

Attribute Database

Attribute 1

Attribute 2

Attribute 3

Server 的典型职责:

职责 示例
保存 Attribute Database GATT DB
接收 Client 请求 ATT_READ_REQATT_WRITE_REQ
返回响应 ATT_READ_RSPATT_WRITE_RSP
检查 handle 是否有效 无效则返回 Invalid Handle
检查读写权限 不允许读写则返回错误
检查安全条件 是否加密、认证、授权
主动通知 Client ATT_HANDLE_VALUE_NTF
主动指示 Client ATT_HANDLE_VALUE_IND

2.4 Client 是什么?

ATT Client 是访问 Server attributes 的一方。

Discover

Read

Write

Response

Notification / Indication

ATT Client

ATT Server

Attribute Database

Client 的典型职责:

职责 示例
发现 attributes Find Information、Read By Type
读取 attribute value Read Request
写入 attribute value Write Request、Write Command
接收 notification Handle Value Notification
接收 indication Handle Value Indication
发送 confirmation Handle Value Confirmation
管理事务状态 Request 发出后等待 Response

2.5 Client/Server 与 Central/Peripheral 的区别

很多初学者最容易把这几个角色搅成一锅粥。

概念 所属层级 说明
Central GAP / Link 层角色 通常发起 BLE 连接
Peripheral GAP / Link 层角色 通常广播并被连接
ATT Client ATT 层角色 访问对端 attribute
ATT Server ATT 层角色 暴露 attribute database
GATT Client GATT 层角色 发起 GATT procedures
GATT Server GATT 层角色 提供 GATT database

它们不是一回事。

典型情况下,手机作为 Central 连接手环,手机通常是 GATT Client / ATT Client,手环通常是 GATT Server / ATT Server。

但这不是强制绑定。

一个设备可以是:

设备 Link 层角色 ATT 角色
手机 Central ATT Client,也可以是 ATT Server
手环 Peripheral ATT Server,也可以是 ATT Client
双模网关 Central / Peripheral 都可能 Client / Server 都可能

所以不要写死:

Central == Client
Peripheral == Server

这在简单 BLE demo 里可能“看起来对”,但在真实协议栈里会把人坑进沟里。


3. Attribute 是一个 discrete value

3.1 原文含义

Protocol overview 说:

An attribute is a discrete value.

这里的 discrete value 可以理解成“独立的数据项”。

ATT 不关心这个数据项在业务上代表什么。
它只关心这个数据项怎么被标识、怎么被访问、有没有权限限制。

3.2 Attribute 的三个关联属性

规范把 attribute 关联的内容列成三项:

属性 中文 说明
attribute type 属性类型 由 UUID 定义
attribute handle 属性句柄 Server 上唯一标识一个 attribute
permissions 权限集合 由使用该 attribute 的上层规范定义

可以画成这样:

Attribute

Attribute Type
UUID

Attribute Handle
16-bit

Permissions
Read / Write / Security

注意,这里看起来少了一个东西:value

因为原文说 attribute 本身是一个 discrete value,同时又有 type、handle、permissions 这些关联属性。
从工程建模上,我们通常会把它整理成:

Attribute = Handle + Type + Value + Permissions

也就是:

Attribute

Handle

Type / UUID

Value

Permissions

3.3 工程中的 Attribute 数据结构

一个最小 ATT/GATT 数据库项,大概会写成这样:

typedef struct {
    uint16_t handle;
    uint8_t  uuid_type;      // 16-bit / 32-bit / 128-bit
    uint8_t  uuid[16];
    uint8_t *value;
    uint16_t value_len;
    uint16_t permissions;
} att_attribute_t;

这个结构不是规范原文规定的 C 结构,只是工程实现中常见的抽象。


4. Attribute Type:说明这个属性“是什么”

4.1 原文含义

Protocol overview 说明:

Attribute type specifies what the attribute represents.

也就是说,Attribute Type 负责说明这个 attribute 代表什么。

4.2 Attribute Type 与 UUID

Attribute Type 由 UUID 定义。

UUID 类型 用途
16-bit UUID Bluetooth SIG 分配的常用类型
32-bit UUID 可扩展类型,需要映射到 128-bit UUID
128-bit UUID 厂商自定义类型常用

例如:

Attribute Type 说明
0x2800 Primary Service Declaration
0x2803 Characteristic Declaration
0x2902 Client Characteristic Configuration Descriptor
0x2A00 Device Name
0x2A19 Battery Level

这里要注意:

UUID 说明 attribute 是什么,handle 说明 attribute 在哪里。

4.3 通俗理解

如果 Attribute Database 是一个小区:

ATT 概念 类比
Attribute Handle 门牌号
Attribute Type / UUID 房屋用途
Attribute Value 房子里放的东西
Permissions 门禁规则

例如:

Handle UUID Value
0x0001 0x2800 Primary Service GAP Service UUID
0x0002 0x2803 Characteristic Declaration Device Name 声明
0x0003 0x2A00 Device Name "MyDevice"

Client 想读 Device Name 时,不是直接说:

我要读 Device Name

而是说:

ATT_READ_REQ(handle = 0x0003)

UUID 帮你发现它,handle 帮你访问它。


5. Bluetooth SIG defined attribute types 与 Non-Bluetooth SIG types

5.1 原文含义

Protocol overview 还提到两类 attribute type:

类型 说明
Bluetooth SIG defined attribute types 由 Bluetooth SIG 定义,记录在 Assigned Numbers 中
Non-Bluetooth SIG attribute types 非 Bluetooth SIG 定义,通常是厂商自定义 UUID

5.2 工程解释

如果你做标准服务,比如 Battery Service,那么会使用 SIG 定义的 UUID:

服务 / 特征值 UUID
Battery Service 0x180F
Battery Level 0x2A19

如果你做私有 OTA 服务,就可能定义 128-bit UUID:

12345678-1234-5678-1234-56789abcdef0

5.3 常见坑

厂商自定义 UUID 最容易出问题的是字节序。

很多抓包工具显示 UUID 的方式和代码数组里的存储方式不一致。
你以为自己写的是:

12345678-1234-5678-1234-56789abcdef0

结果代码里数组小端排布,看起来像被蓝牙规范放进洗衣机甩干了一遍。

工程建议:

  • 统一封装 UUID 比较函数;

  • 不要到处手写 memcmp

  • 打日志时同时打印 canonical string 和 raw bytes;

  • 抓包和代码里的 UUID 表示方式要做对照表。


6. Attribute Handle:Client 访问 attribute 的关键

6.1 原文含义

Protocol overview 说明:

Attribute handle uniquely identifies an attribute on a server.

也就是说,在同一个 ATT Server 上,handle 唯一标识一个 attribute。

6.2 handle 的作用

handle 用在下面几个场景:

场景 例子
Read Request Client 通过 handle 读取 value
Write Request Client 通过 handle 写入 value
Notification Server 告诉 Client 哪个 handle 的 value 变化
Indication Server 指示 Client 哪个 handle 的 value 变化
Find / Discover Client 发现 Server 上有哪些 handle

6.3 handle 和读写操作

普通读:

ATT Server ATT Client ATT Server ATT Client ATT_READ_REQ(handle = 0x0003) ATT_READ_RSP(value)

普通写:

ATT Server ATT Client ATT Server ATT Client ATT_WRITE_REQ(handle = 0x0008, value = 0x0100) ATT_WRITE_RSP

Notification:

ATT Server ATT Client ATT Server ATT Client ATT_HANDLE_VALUE_NTF(handle = 0x0007, value = 87)

6.4 handle 的工程意义

handle 是 ATT 层的核心索引。

ATT Server 收到请求后,第一步通常是查 handle:

att_attribute_t *att_db_find_by_handle(uint16_t handle)
{
    for (int i = 0; i < att_db_count; i++) {
        if (att_db[i].handle == handle) {
            return &att_db[i];
        }
    }

    return NULL;
}

如果找不到,后续就会返回类似:

Invalid Handle

或者在某些 discovery 场景返回:

Attribute Not Found

具体返回什么,要看对应 PDU 的规范要求。


7. Permissions:权限存在,但不能通过 ATT 直接访问

7.1 原文含义

Protocol overview 里有一句非常重要:

permissions are defined by each higher layer specification that utilizes the attribute; these permissions cannot be accessed using the Attribute Protocol.

意思是:

  • Attribute 的 permissions 由使用它的上层规范定义;

  • ATT 本身不能直接读取 permissions。

7.2 逐句解释

原文含义 中文解释 工程理解
permissions are defined by higher layer specification 权限由上层规范定义 比如 GATT、Service 规范、Profile 规范
utilizes the attribute 使用该 attribute 的规范 谁定义这个 attribute,谁规定它怎么访问
cannot be accessed using ATT 不能通过 ATT 直接访问 permissions Client 不能发一个 ATT 命令直接查询权限表

7.3 什么叫不能通过 ATT 访问 permissions?

这点很容易误解。

Client 不能这样问 Server:

请告诉我 handle 0x0003 的权限是什么?

ATT 没有这种通用 PDU。

但 Client 可以通过访问结果间接知道:

操作 可能结果
读一个不可读 attribute 返回 Read Not Permitted
写一个不可写 attribute 返回 Write Not Permitted
未加密读取安全 attribute 返回 Insufficient Encryption
未认证访问 MITM 保护 attribute 返回 Insufficient Authentication
密钥长度不够 返回 Encryption Key Size Too Short

也就是说,权限像门禁规则。
你不能直接问物业“这扇门权限表打印一份给我”,但你刷卡失败时,门禁会告诉你“大概哪里不行”。

7.4 工程中的权限设计

常见权限 bit 可能这样设计:

#define ATT_PERM_READ              0x0001
#define ATT_PERM_WRITE             0x0002
#define ATT_PERM_READ_ENCRYPT      0x0004
#define ATT_PERM_WRITE_ENCRYPT     0x0008
#define ATT_PERM_READ_AUTHEN       0x0010
#define ATT_PERM_WRITE_AUTHEN      0x0020
#define ATT_PERM_AUTHORIZE         0x0040

读请求处理时可能这样:

static uint8_t att_check_read_permission(att_attribute_t *attr,
                                         att_security_t *sec)
{
    if ((attr->permissions & ATT_PERM_READ) == 0) {
        return ATT_ERR_READ_NOT_PERMITTED;
    }

    if ((attr->permissions & ATT_PERM_READ_ENCRYPT) &&
        !sec->encrypted) {
        return ATT_ERR_INSUFFICIENT_ENCRYPTION;
    }

    if ((attr->permissions & ATT_PERM_READ_AUTHEN) &&
        !sec->authenticated) {
        return ATT_ERR_INSUFFICIENT_AUTHENTICATION;
    }

    return ATT_ERR_SUCCESS;
}

7.5 常见坑

权限不在 ATT PDU 里,但 ATT Server 处理每个 PDU 时都要查权限。

这就像餐厅菜单上不一定写“厨房是否允许你进”,但你真往厨房冲,保安一定会拦你。


8. Client 发送 Request,Server 必须响应

8.1 原文含义

Protocol overview 明确说明:

A client may send Attribute Protocol requests to a server, and the server shall respond to all requests that it receives.

这句话里的 shall 很重要。

8.2 逐句解释

原文含义 中文解释 工程意义
Client may send requests Client 可以发送 ATT requests Client 主动访问 Server
Server shall respond Server 必须响应 收到 Request 不能装死
all requests it receives 所有收到的 requests 不支持也要用 Error Response 回

8.3 Request 和 Response 的基本模型

ATT Server ATT Client ATT Server ATT Client alt [成功] [失败] ATT_XXX_REQ ATT_XXX_RSP ATT_ERROR_RSP

比如读请求:

ATT Server ATT Client ATT Server ATT Client alt [handle 有效且允许读] [handle 无效或权限不满足] ATT_READ_REQ(handle) ATT_READ_RSP(value) ATT_ERROR_RSP

8.4 工程注意

只要是 Request,就必须有结果:

Request 情况 Server 应该做什么
支持且成功 返回对应 Response
支持但失败 返回 ATT_ERROR_RSP
不支持该 Request 返回 ATT_ERROR_RSP,错误码一般是 Request Not Supported
PDU 格式非法 返回 ATT_ERROR_RSP 或按规范处理
权限不足 返回对应权限错误
handle 不存在 返回对应 handle/attribute 错误

不能这样:

收到不支持的 request,直接丢弃

这会导致 Client 等超时。
协议栈里最怕这种“沉默是金”的行为,因为调试时你会看到 Client 像等对象回消息一样,越等越焦虑。


9. 一个设备可以同时是 Client 和 Server

9.1 原文含义

Protocol overview 说明:

A device can implement both client and server roles, and both roles can function concurrently in the same device and between the same devices.

意思是:

  • 一个设备可以同时实现 Client 和 Server;

  • 两个角色可以在同一个设备里并发工作;

  • 同一对设备之间,也可以双向同时存在 Client/Server 关系。

9.2 图示

Device B Device A Device B Device A A 作为 Client 访问 B 的 Server B 也可以作为 Client 访问 A 的 Server ATT_READ_REQ ATT_READ_RSP ATT_WRITE_REQ ATT_WRITE_RSP

9.3 工程解释

这意味着协议栈不能假设:

我连接对方后,只会作为 Client

或者:

我是 Peripheral,所以我只会作为 Server

真实产品里可能出现:

场景 说明
手机读手环电量 手机是 Client,手环是 Server
手环读手机时间服务 手环是 Client,手机是 Server
两个设备互相同步状态 双方都同时是 Client 和 Server
EATT 多 bearer 并发 多个 ATT bearer 同时存在事务

9.4 工程设计建议

协议栈内部最好拆分:

ATT Layer

ATT Client Engine

ATT Server Engine

ATT Bearer Manager

Attribute Database

TX Scheduler

Client 和 Server 状态机不要写成互斥。

否则一旦遇到双向 GATT 或 EATT,就会出现各种玄学 bug。


10. 每个 Bluetooth Device 只有一个 Server 实例

10.1 原文含义

Protocol overview 中有一个非常关键的约束:

There shall be only one instance of a server on each Bluetooth device.

也就是说:

每个蓝牙设备上只能有一个 ATT Server 实例。

这个点很重要,尤其是理解多 bearer 和 GATT database 时。

10.2 为什么只能有一个 Server?

因为 handle namespace 必须一致。

规范进一步说明,这意味着:

attribute handles shall be identical for all supported bearers.

也就是说,同一个设备如果有多个 ATT bearer,不管你从哪个 bearer 访问,看到的 handle 应该是一致的。

10.3 图示

Bluetooth Device

One ATT Server Instance

One Attribute Database

ATT Bearer 1

ATT Bearer 2

ATT Bearer 3

错误理解:

Bluetooth Device

ATT Server 1

ATT Server 2

DB 1

DB 2

Bearer 1

Bearer 2

后面这种设计容易导致:

  • 同一个 handle 在不同 bearer 上含义不一样;

  • GATT cache 混乱;

  • EATT 并发访问不一致;

  • BR/EDR bearer 和 LE bearer 看到的数据库不一致;

  • 对端 Client 怀疑人生。

10.4 工程意义

如果一个设备同时支持:

  • LE fixed ATT bearer;

  • LE EATT bearer;

  • BR/EDR ATT bearer;

它们看到的 handle 应该一致。

也就是说:

Bearer handle 0x0003 应该代表
LE fixed ATT bearer Device Name
LE EATT bearer Device Name
BR/EDR ATT bearer Device Name

不能出现:

Bearer handle 0x0003
LE fixed ATT bearer Device Name
EATT bearer Battery Level
BR/EDR bearer Firmware Version

那就不是协议栈,是“平行宇宙”。


11. 对于同一个 Client,Server 有一套 attributes

11.1 原文含义

Protocol overview 还说明:

For a given client, the server shall have one set of attributes, which shall have the same value and properties irrespective of which bearer is used.

这句话可以拆成三层理解:

要点 中文解释
For a given client 对于某一个特定 Client
one set of attributes Server 有一套 attributes
same value and properties 不管用哪个 bearer,value 和 properties 应该一致

11.2 举例说明

假设 Client A 通过两个 ATT bearer 访问 Server:

Client A

ATT Bearer 1

ATT Bearer 2

ATT Server

Attribute Set for Client A

如果 Client A 通过 Bearer 1 读取 handle 0x0007,得到 Battery Level = 87。
那么它通过 Bearer 2 读取同一个 handle,应该看到同一套 attribute 视图。

11.3 “same value” 和 “per-client value” 不矛盾

后面原文还提到:

Server can support multiple clients. Attribute values can be the same or different for each client as defined by GATT or higher layer specification.

也就是说:

  • 对同一个 Client,跨 bearer 视图一致;

  • 对不同 Client,某些 attribute value 可以不同,前提是由 GATT 或上层规范定义。

11.4 举例:CCCD 就是典型 per-client value

CCCD,全称 Client Characteristic Configuration Descriptor。

它通常是每个 Client 独立保存的。

例如:

Client CCCD value 含义
Client A 0x0001 开启 Notification
Client B 0x0000 未开启 Notification
Client C 0x0002 开启 Indication

这时候不同 Client 看到的 CCCD value 可以不同。

但是对于 Client A 自己,不管它通过哪个 ATT bearer 访问,CCCD 视图应该一致。

11.5 工程数据结构建议

可以把 attribute 分成两类:

类型 说明
Global Attribute Value 所有 Client 共享
Per-client Attribute Value 每个 Client 独立

示例:

typedef struct {
    uint16_t conn_id;
    uint16_t handle;
    uint8_t *value;
    uint16_t value_len;
} att_client_value_t;

对于 CCCD 这种 per-client descriptor,建议不要简单存在全局 GATT DB 里,否则多个手机连接同一个设备时,通知配置会互相串台。

串台的后果就是:

A 手机打开通知,B 手机也收到;
B 手机关闭通知,A 手机突然没数据。
这不是蓝牙,是宫斗剧。


12. Server 可以支持多个 Clients

12.1 原文含义

Protocol overview 明确说 Server 可以支持多个 clients。

这意味着一个 ATT Server 可能同时被多个连接访问。

ATT Server

Attribute Database

Client A

Client B

Client C

12.2 工程挑战

多个 Client 带来几个问题:

问题 说明
并发访问 多个 Client 同时读写 attribute
per-client 状态 CCCD、MTU、安全状态可能不同
Notification 目标 每个 Client 是否开启通知不同
安全等级 不同连接加密/认证状态可能不同
Bearer 管理 每个连接可有多个 ATT bearer
资源限制 Prepare Queue、TX buffer、回调上下文

12.3 最小连接上下文

协议栈里通常需要维护 per-client / per-connection 上下文:

typedef struct {
    uint16_t conn_handle;
    uint16_t att_mtu;
    bool encrypted;
    bool authenticated;
    uint8_t encryption_key_size;

    /* per-client CCCD or other values */
    att_client_value_t client_values[MAX_CLIENT_VALUES];

    /* request transaction state */
    bool has_pending_req;
    uint8_t pending_opcode;
} att_client_context_t;

注意这里的 client 不是 ATT Client Engine,而是“连接到本 Server 的对端 Client 上下文”。

命名要谨慎,不然代码里会出现:

client->client->server_client->client_role

这种变量名一出来,后来的维护者基本只能烧香。


13. 多个 Services 可以暴露在一个 Server 上

13.1 原文 note 含义

Protocol overview 的 note 说明:

Multiple services may be exposed on a single server by allocating separate ranges of handles for each service.

意思是:

一个 ATT Server 可以暴露多个 services,每个 service 分配不同的 handle range。

13.2 图示

ATT Server

Attribute Database

GAP Service
0x0001 - 0x0005

GATT Service
0x0006 - 0x0009

Battery Service
0x000A - 0x000D

Vendor OTA Service
0x0010 - 0x0018

13.3 示例 Attribute Database

Handle Range Service 示例
0x0001–0x0005 GAP Service Device Name、Appearance
0x0006–0x0009 GATT Service Service Changed
0x000A–0x000D Battery Service Battery Level
0x0010–0x0018 Vendor OTA Service OTA Control、OTA Data

13.4 Service Discovery 的本质

GATT 服务发现本质上就是:

在 Attribute Database 里找出哪些 handle range 对应哪些 service。

常见 ATT PDU 是:

ATT_READ_BY_GROUP_TYPE_REQ
ATT_READ_BY_GROUP_TYPE_RSP

这个我们后面会在 Read By Group Type 一篇详细讲。


14. Notification 和 Indication:不需要 Client 主动 Read

14.1 原文含义

Protocol overview 说明:

ATT 有 notification 和 indication 能力,可以高效地把 attribute values 发送给 Client,而不需要 Client 主动读取。

这点很关键。

如果没有 Notification/Indication,Client 想知道数据变化,就只能不断 read:

Server Client Server Client loop [轮询] ATT_READ_REQ ATT_READ_RSP

这很浪费。

有了 Notification:

Server Client Server Client Client 配置 CCCD 开启通知 ATT_HANDLE_VALUE_NTF(value changed) ATT_HANDLE_VALUE_NTF(value changed) ATT_HANDLE_VALUE_NTF(value changed)

有了 Indication:

Server Client Server Client ATT_HANDLE_VALUE_IND(value changed) ATT_HANDLE_VALUE_CFM

14.2 Notification vs Indication

Notification Indication
ATT PDU ATT_HANDLE_VALUE_NTF ATT_HANDLE_VALUE_IND
是否需要 Client 确认
是否适合高频数据 不太适合
是否适合关键状态 一般 适合
典型用途 传感器数据、音频控制状态、日志流 关键配置结果、重要状态变化
性能 较低
可靠性语义 ATT 层无确认 ATT 层有确认

14.3 工程提醒

Notification 不等于“不可靠传输”。
底层链路可能仍然有重传和确认机制。

但从 ATT 层看:

  • Notification 发出去后,不等 ATT_HANDLE_VALUE_CFM

  • Indication 发出去后,必须等 ATT_HANDLE_VALUE_CFM

所以 ATT 层语义不同。


15. ATT bearer:ATT PDU 不是凭空飞过去的

15.1 原文含义

Protocol overview 后半部分说明:

All Attribute Protocol requests are sent over an ATT bearer.

也就是说,ATT 请求都要走 ATT bearer。

15.2 ATT bearer 是什么?

ATT bearer 是发送 ATT PDU 的通道。
它底层使用 L2CAP channel。

ATT PDU

ATT Bearer

L2CAP Channel

Physical / Logical Link

15.3 多个 ATT bearer

Protocol overview 说明,两个设备之间可以建立多个 ATT bearers。

每个 ATT bearer:

  • 使用单独的 L2CAP channel;

  • 可以有不同配置;

  • 可以支持不同的并发行为;

  • 是 EATT 能力的基础。

ATT Bearer 1 / L2CAP Channel 1

ATT Bearer 2 / L2CAP Channel 2

ATT Bearer 3 / L2CAP Channel 3

Device A

Device B

15.4 为什么需要多个 bearer?

传统 ATT bearer 上,很多事务是顺序处理的。
一个 request 没完成,后面的 request 不能随便插队。

这就像一个银行窗口:

客户 A 正在办大额转账
后面的人只能排队

EATT / Enhanced ATT bearer 的意义之一,就是增加多个窗口:

窗口 1:读设备名
窗口 2:写 OTA 数据
窗口 3:读电池电量

这样能降低阻塞,提高并发能力。


16. LE 上的 ATT bearer

16.1 原文含义

Protocol overview 说明:

In LE, there is a single ATT bearer that uses a fixed channel that is available as soon as the ACL connection is established.

意思是:

在 LE 中,ACL 连接一建立,就有一个默认可用的 ATT bearer。
这个 bearer 使用固定 L2CAP channel。

16.2 图示

LE Device B LE Device A LE Device B LE Device A 默认 ATT bearer 可用 LE ACL Connection Established ATT_EXCHANGE_MTU_REQ ATT_EXCHANGE_MTU_RSP

16.3 工程意义

BLE 连接建立后,通常就可以开始:

  • Exchange MTU;

  • Service Discovery;

  • Characteristic Discovery;

  • Read;

  • Write;

  • Configure CCCD;

  • Notification / Indication。

路径如下:

ATT

L2CAP Fixed CID
LE ATT

HCI ACL

LE Link Layer

这也是为什么 BLE GATT 使用起来相对直接:
连接一建立,ATT/GATT 的基础通道就已经在那里了。

像进餐厅,桌子已经摆好了,你可以直接点菜。


17. LE 上的 additional ATT bearers

17.1 原文含义

Protocol overview 还说:

Additional ATT bearers can be established using L2CAP.

也就是说,除了默认 fixed ATT bearer,LE 还可以通过 L2CAP 建立额外 ATT bearers。

这就是 EATT 的基础。

17.2 图示

LE ACL Connection

Fixed ATT Bearer
默认存在

Enhanced ATT Bearer 1
L2CAP Dynamic Channel

Enhanced ATT Bearer 2
L2CAP Dynamic Channel

17.3 工程意义

默认 ATT bearer 像一个窗口。
EATT 像多开几个窗口。

场景 默认 ATT EATT
简单读写 足够 不一定需要
大量并发 GATT 操作 容易阻塞 更合适
多服务并行操作 事务排队 可并发
OTA + 控制命令并行 可能互相影响 更容易优化
多客户端复杂场景 状态管理复杂 仍复杂,但能力更强

注意:EATT 不是“免费加速器”。
它引入更多 bearer、更多状态机、更多流控、更多资源管理。
想吃多线程红利,就要先交并发 bug 的学费。


18. BR/EDR 上的 ATT bearer

18.1 原文含义

Protocol overview 说明:

In BR/EDR, one or more ATT bearers can be established using L2CAP.

也就是说,BR/EDR 上没有 LE 那种“ACL 建立后自动可用的 fixed ATT bearer”。
它需要通过 L2CAP 建立一个或多个 ATT bearers。

18.2 图示

BR/EDR Device B BR/EDR Device A BR/EDR Device B BR/EDR Device A BR/EDR ACL Connection L2CAP Channel Establishment for ATT L2CAP Channel Established ATT PDU over L2CAP Channel

18.3 工程意义

ATT/GATT 不是 BLE 专属。
BR/EDR 也可以承载 ATT/GATT,但承载方式不同。

对比:

LE BR/EDR
默认 ATT bearer ACL 建立后 fixed channel 可用 无默认 fixed ATT bearer
额外 bearer 通过 L2CAP 建立 通过 L2CAP 建立
常见使用 BLE GATT GATT over BR/EDR
底层链路 LE ACL BR/EDR ACL
典型场景 BLE 服务访问 双模设备、GATT over BR/EDR

19. ATT bearer 与 Client/Server 的完整关系

把前面的内容合起来:

Device B

Device A

ATT Client

ATT Server

Attribute Database A

ATT Client

ATT Server

Attribute Database B

ATT Bearer 1
L2CAP Channel

ATT Bearer 2
L2CAP Channel

关键点:

  1. 一个设备可以同时有 ATT Client 和 ATT Server。

  2. 一个设备上只有一个 ATT Server 实例。

  3. 两个设备之间可以有多个 ATT bearer。

  4. 每个 ATT bearer 对应独立 L2CAP channel。

  5. PDU 类型决定它应该交给本地 Client 还是 Server 处理。


20. 从抓包角度看 Protocol Overview

ATT 在 HCI 抓包里通常长这样:

HCI ACL Data
  └── L2CAP
      └── ATT
          ├── Opcode
          ├── Parameters
          └── Optional Signature

20.1 LE 默认 ATT bearer 抓包路径

LE ACL Connection
  HCI ACL Data
    L2CAP CID: ATT fixed channel
      ATT_READ_REQ

20.2 BR/EDR ATT bearer 抓包路径

BR/EDR ACL Connection
  L2CAP Dynamic Channel Establishment
  HCI ACL Data
    L2CAP Dynamic CID
      ATT PDU

20.3 抓包判断思路

看到一个 ATT 包,先问 5 个问题:

问题 作用
这个 PDU 是什么类型? Request、Response、Command、Notification、Indication、Confirmation
方向是谁到谁? Client → Server 还是 Server → Client
走哪个 L2CAP CID? fixed channel 还是 dynamic channel
操作哪个 handle? 访问哪个 attribute
是否需要后续响应? 判断事务是否完整

21. Protocol Overview 的工程版状态机

21.1 ATT Server 简化状态机

Receive ATT Request

Check Handle

Handle Valid

Invalid Handle

Permission OK

Permission / Security Error

Idle

ReceiveRequest

CheckHandle

CheckPermission

SendError

ProcessRequest

SendResponse

21.2 ATT Client 简化状态机

Send ATT Request

Receive Response

Receive Error Response

Response Timeout

Idle

SendRequest

WaitResponse

Complete

Error

Timeout

21.3 Notification / Indication 状态机

Send Notification

No Confirmation Needed

Send Indication

Receive Confirmation

Confirmation Timeout

Idle

SendNotification

SendIndication

WaitConfirmation

Timeout


22. 实现一个 ATT Server 时,这一节给出的约束

从 Protocol Overview 可以抽出几个实现约束:

规范要求 工程实现
Server 暴露 attributes 需要 Attribute Database
Attribute 有 type DB 中要保存 UUID
Attribute 有 handle DB 中要保存唯一 handle
Attribute 有 permissions DB 中要保存权限和安全要求
Client 通过 handle 访问 Server 要支持 handle lookup
Request 必须响应 Server 不能丢 request
一个设备只有一个 Server 实例 多 bearer 共享同一 DB
多 bearer handle 一致 不同 bearer 看到同一 handle namespace
支持多个 clients 需要 per-connection context
不同 client value 可不同 CCCD 等要 per-client 存储
ATT 走 bearer 需要 bearer manager
LE 有默认 fixed bearer ACL 建立后初始化默认 ATT bearer
BR/EDR 通过 L2CAP 建 bearer 需要 L2CAP channel establishment

23. 最小 ATT Server 架构

L2CAP RX

ATT Bearer Manager

ATT PDU Parser

Opcode Dispatcher

Attribute Database

Permission Check

Security Context

Response Builder

Notification / Indication Manager

L2CAP TX

23.1 核心结构建议

typedef struct {
    uint16_t cid;
    uint16_t mtu;
    bool enhanced;
} att_bearer_t;

typedef struct {
    uint16_t handle;
    att_uuid_t type;
    uint8_t *value;
    uint16_t value_len;
    uint16_t permissions;
} att_attribute_t;

typedef struct {
    uint16_t conn_handle;
    bool encrypted;
    bool authenticated;
    uint8_t key_size;
} att_security_context_t;

typedef struct {
    uint16_t conn_handle;
    att_bearer_t bearers[MAX_ATT_BEARERS];
    att_security_context_t security;
} att_connection_t;

23.2 Request 处理框架

void att_server_handle_request(att_connection_t *conn,
                               att_bearer_t *bearer,
                               const uint8_t *pdu,
                               uint16_t len)
{
    uint8_t opcode = pdu[0];

    switch (opcode) {
    case ATT_READ_REQ:
        att_handle_read_req(conn, bearer, pdu, len);
        break;

    case ATT_WRITE_REQ:
        att_handle_write_req(conn, bearer, pdu, len);
        break;

    case ATT_FIND_INFORMATION_REQ:
        att_handle_find_information_req(conn, bearer, pdu, len);
        break;

    default:
        att_send_error_rsp(bearer,
                           opcode,
                           0x0000,
                           ATT_ERR_REQUEST_NOT_SUPPORTED);
        break;
    }
}

24. 本篇重点术语表

术语 中文 工程理解
Server role 服务端角色 暴露 Attribute Database
Client role 客户端角色 访问 Server 的 attributes
Attribute 属性 ATT 数据访问基本单位
Attribute Type 属性类型 UUID,说明 attribute 是什么
Attribute Handle 属性句柄 Server 内唯一编号
Permissions 权限 由上层定义,ATT 不能直接读取
ATT bearer ATT 承载 发送 ATT PDU 的 L2CAP channel
Fixed channel 固定信道 LE 默认 ATT bearer 使用
Dynamic channel 动态信道 L2CAP 建立的额外 bearer
Notification 通知 Server 主动发,不要确认
Indication 指示 Server 主动发,需要确认
Confirmation 确认 Client 对 indication 的确认

25. 常见误区

25.1 误区一:Client 一定是 Central

不一定。

Central/Peripheral 是连接角色。
Client/Server 是 ATT/GATT 访问角色。

25.2 误区二:一个 bearer 对应一套 DB

不对。

同一个设备只有一个 ATT Server 实例。
多个 bearer 应该共享一致的 handle namespace 和 attribute 视图。

25.3 误区三:不同 Client 的所有 value 必须完全一样

不一定。

某些 value 可以按 Client 不同而不同,例如 CCCD。
但这必须由 GATT 或上层规范定义。

25.4 误区四:权限可以通过 ATT 查询

不能。

ATT 没有一个“读取权限表”的通用 PDU。
Client 只能通过访问结果和错误码间接知道权限约束。

25.5 误区五:Notification 和 Indication 都是 Server 主动发,所以差不多

不一样。

Notification 不需要 Confirmation。
Indication 需要 Client 回 Confirmation。


26. 本篇小结

本篇学习了 Vol 3 Part F 的 2 Protocol overview,核心结论如下:

  1. ATT 定义两种角色:Client 和 Server。

  2. Server 暴露 attributes,Client 访问 attributes。

  3. Attribute 是独立数据项,关联 type、handle、permissions。

  4. Attribute Type 由 UUID 定义,用来说明 attribute 代表什么。

  5. Attribute Handle 在 Server 上唯一标识一个 attribute。

  6. Permissions 由上层规范定义,不能通过 ATT 直接访问。

  7. Client 可以发送 requests,Server 必须响应收到的 requests。

  8. 一个设备可以同时实现 Client 和 Server。

  9. 一个 Bluetooth Device 上只能有一个 ATT Server 实例。

  10. 多个 ATT bearers 应该看到一致的 attribute handle namespace。

  11. Server 可以支持多个 clients。

  12. 某些 attribute value 可以按 Client 不同而不同,例如 CCCD。

  13. 多个 services 可以通过不同 handle range 暴露在同一个 Server 上。

  14. Notification 和 Indication 允许 Server 主动向 Client 发送 attribute value。

  15. 所有 ATT 请求都通过 ATT bearer 发送。

  16. 一个 ATT bearer 使用一个 L2CAP channel。

  17. LE 中默认有一个 fixed ATT bearer,ACL 连接建立后即可使用。

  18. LE 可以通过 L2CAP 建立额外 ATT bearers。

  19. BR/EDR 中可以通过 L2CAP 建立一个或多个 ATT bearers。

这一节是 ATT 的“协议地图”。
后面进入 Protocol requirements 时,就会开始详细拆 Attribute type、handle、value、permissions、MTU、bearer 等基础概念。

下一篇继续学习:

第 3 篇:Protocol Requirements 总览——ATT 规范里的强制要求

下一篇重点:

  • 3 Protocol requirements;

  • 3.1 Introduction;

  • Attribute 的基本访问规则;

  • shallmayshall not 的工程含义;

  • 为什么 ATT 的很多权限和语义都由上层规范决定。

Logo

免费领 150 小时云算力,进群参与显卡、AI PC 幸运抽奖

更多推荐