【Bluetooth-SIG】【CoreV6.2】【Vol3 Part F】【三】【Protocol Requirements 总览——ATT 规范里的强制要求】
Core_v6.2 Vol 3 Part F 学习笔记(三):Protocol Requirements 总览——ATT 规范里的强制要求
0. 本篇说明
本文继续学习 Bluetooth Core Specification v6.2:
Vol 3, Part F:Attribute Protocol,简称 ATT
本篇对应章节:
3 Protocol requirements
3.1 Introduction
上一篇我们学习了 2 Protocol overview,知道 ATT 是一个 Client/Server 模型:
-
Server 暴露 attributes;
-
Client 访问 attributes;
-
一个设备可以同时是 Client 和 Server;
-
ATT PDU 通过 ATT bearer 发送;
-
LE 默认有 fixed ATT bearer;
-
BR/EDR 可以通过 L2CAP 建立 ATT bearer。
这一篇开始进入 Protocol requirements,也就是 ATT 的“硬规则区”。
如果说前两篇是在看蓝牙小区地图,那么这一篇开始就像进了物业办公室,看门牌、产权、门禁、钥匙、保安规则。
ATT 里最核心的几个对象是:
Attribute Type
Attribute Handle
Attribute Value
Attribute Permissions
它们之间的关系可以先画成这样:
一句话先总结:
Attribute Type 告诉 Client:“这是什么”;Attribute Handle 告诉 Client:“它在哪里”;Attribute Value 是“里面的数据”;Permissions 决定“你有没有资格读写”。
1. 3 Protocol requirements 是什么?
1.1 章节定位
Protocol requirements 这一章不是在讲某一个具体 PDU,而是在定义 ATT 协议的基础规则。
后面的所有 PDU,比如:
-
ATT_READ_REQ -
ATT_WRITE_REQ -
ATT_FIND_INFORMATION_REQ -
ATT_READ_BY_TYPE_REQ -
ATT_HANDLE_VALUE_NTF
都要基于这些规则工作。
可以这样理解:
1.2 这一章后续会展开什么?
后续章节会逐个讲:
| 小节 | 内容 | 作用 |
|---|---|---|
| 3.1 Introduction | 总体介绍 | 说明 Attribute 的 type、handle、value、permissions |
| 3.2.1 Attribute type | 属性类型 | 用 UUID 标识 attribute 是什么 |
| 3.2.2 Attribute handle | 属性句柄 | 用 16-bit handle 访问 attribute |
| 3.2.3 Attribute handle grouping | handle 分组 | 用于服务、分组属性 |
| 3.2.4 Attribute value | 属性值 | 一串 octet 数据 |
| 3.2.5 Attribute permissions | 属性权限 | 控制读写、安全要求 |
| 3.2.6 Control-point attributes | 控制点属性 | 常见于写控制命令 |
| 3.2.7 Protocol methods | 协议方法 | Request、Command、Notification 等 |
| 3.2.8 Exchanging MTU size | MTU 交换 | 决定 ATT PDU 最大长度 |
| 3.2.9 Long attribute values | 长属性值 | 超过一个 PDU 的 value |
| 3.2.10 Atomic operations | 原子操作 | 多 bearer 并发时的访问一致性 |
| 3.2.11 ATT bearers | ATT 承载 | ATT PDU 走哪个 L2CAP channel |
这一篇先讲 3.1,不急着把所有细节一口吞下。
蓝牙规范这东西不能当火锅丸子,一口塞多了容易噎住。
2. 3.1 Introduction 总览
3.1 Introduction 主要说了五件事:
| 编号 | 主题 | 核心意思 |
|---|---|---|
| 1 | Attribute type | 每个 attribute 都有 type,用 UUID 表示它是什么 |
| 2 | Attribute handle | 每个 attribute 都有 handle,用来访问它 |
| 3 | Attribute value | 每个 attribute 都有 value,也就是实际数据 |
| 4 | Handle discovery | Client 通过 ATT PDU 发现 handles |
| 5 | Permissions | Attribute 有权限,控制是否可读、可写、是否需要加密 |
如果画成图:
这一节可以看作 ATT 的“身份证 + 门牌号 + 房间内容 + 门禁规则”说明书。
3. 第一组原文要点:每个 Attribute 都有 Attribute Type
3.1 规范意思
规范说:
每个 attribute 都有一个 attribute type,这个 type 通过 UUID 标识 attribute 代表什么,让 Client 能理解 Server 暴露出来的 attributes。
3.2 逐行翻译
| 原文要点 | 中文翻译 | 工程解释 |
|---|---|---|
| Each attribute has an attribute type | 每个 attribute 都有一个 attribute type | 每个属性都必须有“类型” |
| that identifies what the attribute represents | 用来标识这个 attribute 表示什么 | 告诉 Client 这个属性是设备名、电池电量,还是服务声明 |
| by means of a UUID | 通过 UUID 来标识 | UUID 是 attribute type 的身份标识 |
| so that a client can understand the attributes exposed by a server | 这样 Client 才能理解 Server 暴露的 attributes | Client 通过 UUID 判断这个 handle 对应什么数据 |
3.3 通俗解释
Attribute Type 就像“商品类别”。
比如一个超市货架上有很多商品:
| 商品编号 | 商品类别 | 商品内容 |
|---|---|---|
| 001 | 饮料 | 可乐 |
| 002 | 水果 | 苹果 |
| 003 | 零食 | 薯片 |
在 ATT 里也类似:
| Handle | Attribute Type | Attribute Value |
|---|---|---|
0x0001 |
Primary Service | GAP Service UUID |
0x0002 |
Characteristic Declaration | Device Name 声明 |
0x0003 |
Device Name | "MyDevice" |
0x0004 |
Appearance | 0x0000 |
其中 Attribute Type 告诉你:
这个 attribute 是什么类型。
没有 type,Client 就只能看到一堆 handle 和 value,像在仓库里看到一堆无标签纸箱:
箱子倒是很多,但不知道哪个是键盘,哪个是榴莲,哪个是老板画的大饼。
3.4 UUID 是什么?
UUID,全称:
Universally Unique Identifier
中文通常叫:
通用唯一标识符。
在 ATT 中,UUID 用于标识 attribute type。
常见 UUID:
| UUID | 含义 |
|---|---|
0x2800 |
Primary Service Declaration |
0x2801 |
Secondary Service Declaration |
0x2802 |
Include Declaration |
0x2803 |
Characteristic Declaration |
0x2902 |
Client Characteristic Configuration Descriptor |
0x2A00 |
Device Name |
0x2A19 |
Battery Level |
所以,如果你看到一个 attribute 的 type 是 0x2A19,就知道它大概率是 Battery Level。
4. 第二组原文要点:每个 Attribute 都有 Attribute Handle
4.1 规范意思
规范说:
每个 attribute 都有一个 attribute handle,用于在 server 上访问这个 attribute。
4.2 逐行翻译
| 原文要点 | 中文翻译 | 工程解释 |
|---|---|---|
| Each attribute has an attribute handle | 每个 attribute 都有一个 attribute handle | 每个属性都有一个 handle |
| that is used for accessing the attribute on a server | 这个 handle 用于访问 server 上的 attribute | Client 读写时靠 handle 找到目标 |
| as well as an attribute value | 同时 attribute 还有 attribute value | handle 是地址,value 是数据 |
4.3 Handle 是什么?
Handle 是 ATT 世界里的“门牌号”。
Client 想读某个 attribute,不是直接说:
我要读 Device Name
而是发:
ATT_READ_REQ(handle = 0x0003)
Server 收到后查自己的 Attribute Database:
handle 0x0003 -> Device Name -> "MyDevice"
然后返回:
ATT_READ_RSP(value = "MyDevice")
4.4 Handle 与 UUID 的区别
这两个概念非常容易混。
| 对比项 | Attribute Handle | Attribute Type / UUID |
|---|---|---|
| 作用 | 表示 attribute 在 Server 上的位置 | 表示 attribute 是什么 |
| 类比 | 门牌号 | 房屋用途 |
| 是否唯一 | 同一个 Server 上唯一 | 同一种 UUID 可以出现多次 |
| 用于读写 | 是,Read/Write 通常靠 handle | 通常用于发现和识别 |
| 示例 | 0x0003 |
0x2A00 Device Name |
一句话:
UUID 告诉你“这是什么”,Handle 告诉你“去哪找它”。
4.5 举例:同一个 UUID 可以有多个 handle
规范后面还提到,同一种 attribute type 可以在一个 server 上出现多次。
比如一个设备有两个温度传感器:
| Handle | UUID | Value |
|---|---|---|
0x0010 |
Temperature Measurement | 25.1°C |
0x0020 |
Temperature Measurement | 28.7°C |
UUID 一样,但 handle 不一样。
这说明:
UUID != 唯一实例
Handle = Server 内访问实例的索引
这就像一栋楼里有很多“会议室”,会议室类型一样,但门牌号不同。
你不能只说“我要去会议室”,你得说“我要去 3 楼 301 会议室”。
5. 第三组原文要点:每个 Attribute 都有 Attribute Value
5.1 规范意思
规范说:
每个 attribute 除了 type 和 handle,还有 attribute value。
5.2 逐行翻译
| 原文要点 | 中文翻译 | 工程解释 |
|---|---|---|
| Each attribute has an attribute value | 每个 attribute 都有一个 value | 这是真正被读写的数据 |
| An attribute value is accessed using its attribute handle | attribute value 通过 attribute handle 访问 | Client 不能直接“凭空读 value”,要指定 handle |
| The value can be discovered/read/written through ATT PDUs | value 可通过 ATT PDU 间接操作 | 具体看 PDU 类型和权限 |
5.3 Attribute Value 是什么?
Attribute Value 是一串 octets。
也就是说,对 ATT 来说,它只是字节数组:
0x01
0x57
"MyDevice"
0x01 0x00
0x12 0x34 0x56 0x78
ATT 不理解业务含义。
比如:
| Attribute Type | Value | ATT 是否理解业务含义 |
|---|---|---|
| Device Name | "MyDevice" |
不理解字符串含义,只搬运字节 |
| Battery Level | 0x57 |
不知道这是 87%,只是一个 octet |
| CCCD | 0x01 0x00 |
不直接解释业务,但 GATT 会解释 |
| Vendor OTA Data | 一段固件数据 | 完全看上层定义 |
ATT 就像快递员:
它知道包裹地址、包裹大小、是否能送、是否要签收;
但它不负责拆开包裹研究里面是不是电饭煲。
5.4 Attribute Value 与 GATT 的关系
GATT 会定义 value 的语义。
例如 Battery Level:
| 层级 | 看到的内容 |
|---|---|
| ATT | handle + value bytes |
| GATT | Battery Level Characteristic |
| Battery Service 规范 | 这个 value 表示电量百分比 |
| 应用层 | 显示电量 87% |
所以从抓包上看:
ATT_READ_RSP: 57
ATT 只知道返回了 0x57。
GATT/Service/Application 才知道这是:
Battery Level = 87%
6. 第四组原文要点:Client 通过 ATT PDU 发现 Handles
6.1 规范意思
规范说:
Attribute handles are discovered by a client using Attribute Protocol PDUs.
也就是说,Client 一开始通常不知道 Server 上有哪些 handles,需要通过 ATT PDU 发现。
6.2 逐行翻译
| 原文要点 | 中文翻译 | 工程解释 |
|---|---|---|
| The attribute handles are discovered | attribute handles 是被发现出来的 | Client 通常不能预设对端 handle |
| by a client | 由 Client 发现 | Client 发起 discovery |
| using Attribute Protocol PDUs | 使用 ATT PDUs | 例如 Find Information、Read By Type、Read By Group Type |
6.3 为什么不能直接写死 handle?
在简单 demo 里,你可能知道:
Battery Level handle = 0x0007
于是直接读:
ATT_READ_REQ(0x0007)
但是在真实产品里,这样很危险。
原因:
| 原因 | 说明 |
|---|---|
| 不同设备 GATT DB 不一样 | handle 分配不同 |
| 固件升级可能改变 GATT DB | handle 可能变化 |
| 服务数量不同 | handle range 不同 |
| 有些服务可选 | 是否存在不确定 |
| GATT cache 可能失效 | 需要重新发现 |
所以 Client 正常流程应该是:
6.4 常见 discovery PDU
| 发现目标 | 常见 ATT PDU |
|---|---|
| 发现 handle 与 UUID 映射 | ATT_FIND_INFORMATION_REQ / RSP |
| 按 type + value 查找范围 | ATT_FIND_BY_TYPE_VALUE_REQ / RSP |
| 按 type 读取 attribute | ATT_READ_BY_TYPE_REQ / RSP |
| 发现分组属性,比如 Primary Service | ATT_READ_BY_GROUP_TYPE_REQ / RSP |
GATT 服务发现其实就是基于这些 ATT PDU 组合出来的。
GATT 是导演,ATT 是演员。
导演说“来一段服务发现”,演员实际演的是 READ_BY_GROUP_TYPE。
7. 第五组原文要点:同一种 Attribute Type 可以出现多次
7.1 规范意思
规范说:
Attributes that have the same attribute type may exist more than once in a server.
也就是说,一个 Server 里可以有多个相同 type 的 attributes。
7.2 逐行翻译
| 原文要点 | 中文翻译 | 工程解释 |
|---|---|---|
| Attributes that have the same attribute type | 具有相同 attribute type 的 attributes | UUID 相同 |
| may exist more than once | 可以出现多次 | 不是全局唯一 |
| in a server | 在同一个 Server 上 | 同一个 ATT Server DB 内也可以重复 |
7.3 举例:多个 Characteristic Declaration
一个 GATT Server 里会有很多 Characteristic Declaration,它们的 attribute type 都是:
0x2803
但是 handle 不同:
| Handle | Attribute Type | Value |
|---|---|---|
0x0002 |
0x2803 Characteristic Declaration |
Device Name 声明 |
0x0006 |
0x2803 Characteristic Declaration |
Battery Level 声明 |
0x0011 |
0x2803 Characteristic Declaration |
OTA Control 声明 |
0x0014 |
0x2803 Characteristic Declaration |
OTA Data 声明 |
所以:
同一个 UUID 可以对应多个 handle。
7.4 再举例:多个相同业务类型
假设一个设备有多个相同类型传感器:
| Handle | Type | Value |
|---|---|---|
0x0101 |
Temperature | 左侧温度 |
0x0111 |
Temperature | 右侧温度 |
0x0121 |
Temperature | 环境温度 |
Client 不能只根据 UUID 就认为“只有一个”。
它要根据 handle range 和 discovery 结果来判断。
7.5 工程注意
实现 discovery 时,不能写成:
return first_attribute_with_uuid(uuid);
而应该支持查找多个:
int att_find_all_by_type(uint16_t start,
uint16_t end,
const att_uuid_t *uuid,
att_attribute_t **out,
int max_count);
否则 Client 会发现一半服务,然后以为对端“少了半边脑子”。
8. 第六组原文要点:Attributes 有 Permissions
8.1 规范意思
规范说:
Attributes have a set of permissions that controls whether they can be read or written, or whether the attribute value shall be sent over an encrypted link.
也就是说,每个 attribute 可以有一组权限,用于控制:
-
是否可读;
-
是否可写;
-
是否必须通过加密链路传输;
-
是否需要认证;
-
是否需要授权;
-
是否有密钥长度要求。
8.2 逐行翻译
| 原文要点 | 中文翻译 | 工程解释 |
|---|---|---|
| Attributes also have a set of permissions | attributes 还有一组 permissions | 每个 attribute 有访问控制规则 |
| controls whether they can be read | 控制是否可以读 | 不可读则返回 Read Not Permitted |
| or written | 控制是否可以写 | 不可写则返回 Write Not Permitted |
| whether the attribute value shall be sent over an encrypted link | 控制 value 是否必须通过加密链路发送 | 如果没加密,访问会失败 |
| Security aspects are defined in Section 4 | 安全相关内容在第 4 节定义 | 后面 Security considerations 会详细讲 |
8.3 Permissions 是 Server 本地规则
Permissions 通常不会直接暴露给 Client。
Client 不能发一个 PDU:
请告诉我 handle 0x0007 的权限表。
ATT 没有这种通用查询权限表的 PDU。
Client 只能通过访问结果间接知道:
| Client 操作 | Server 可能返回 |
|---|---|
| 读不可读属性 | Read Not Permitted |
| 写不可写属性 | Write Not Permitted |
| 未加密访问加密属性 | Insufficient Encryption |
| 未认证访问认证属性 | Insufficient Authentication |
| 密钥长度不足 | Encryption Key Size Too Short |
| 未授权 | Insufficient Authorization |
8.4 权限像门禁
Attribute Database 可以类比成一栋楼:
| ATT 概念 | 类比 |
|---|---|
| Attribute Handle | 房间门牌号 |
| Attribute Type | 房间用途 |
| Attribute Value | 房间里的东西 |
| Permissions | 门禁规则 |
| Encryption | 保安通道 |
| Authentication | 人脸识别 |
| Authorization | 领导批准 |
你知道房间号,不代表你能进。
你能进,不代表你能搬东西。
你能看,不代表你能改。
蓝牙权限也是这个逻辑。
9. 3.1 Introduction 的完整逻辑图
把 3.1 的内容串起来:
可以再抽象成一句工程语言:
Client 先通过 ATT PDU 发现 Server 上有哪些 handle,
再通过 handle 访问 attribute value;
Server 根据 attribute permissions 决定是否允许访问。
10. 3.1 对 ATT Server 实现的要求
从这一小节可以推导出 ATT Server 至少要维护这些信息:
typedef struct {
uint16_t handle;
att_uuid_t type;
uint8_t *value;
uint16_t value_len;
uint16_t permissions;
} att_attribute_t;
10.1 Attribute Database 基本要求
| 字段 | 必要性 | 原因 |
|---|---|---|
| handle | 必须 | Client 通过 handle 访问 |
| type / UUID | 必须 | Client 通过 UUID 理解 attribute |
| value | 必须 | 真实被读写的数据 |
| permissions | 必须 | 控制读写和安全 |
| value length | 必须 | 生成 ATT response 时需要长度 |
| callback | 常见需要 | 动态 value 或控制点处理 |
| per-client storage | 某些 attribute 需要 | CCCD 等 per-client value |
10.2 Server 收到 Read Request 的基本逻辑
伪代码:
void att_handle_read_req(att_connection_t *conn,
att_bearer_t *bearer,
const uint8_t *pdu,
uint16_t len)
{
uint16_t handle;
att_attribute_t *attr;
uint8_t err;
if (len != 3) {
att_send_error_rsp(bearer,
ATT_READ_REQ,
0x0000,
ATT_ERR_INVALID_PDU);
return;
}
handle = read_le16(&pdu[1]);
attr = att_db_find_by_handle(handle);
if (attr == NULL) {
att_send_error_rsp(bearer,
ATT_READ_REQ,
handle,
ATT_ERR_INVALID_HANDLE);
return;
}
err = att_check_read_permission(conn, attr);
if (err != ATT_ERR_SUCCESS) {
att_send_error_rsp(bearer,
ATT_READ_REQ,
handle,
err);
return;
}
att_send_read_rsp(bearer, attr->value, attr->value_len);
}
10.3 Server 收到 Write Request 的基本逻辑
写和读的最大区别是:
| 操作 | 是否修改 value | 成功响应 |
|---|---|---|
| Read | 不修改 | ATT_READ_RSP |
| Write Request | 修改 | ATT_WRITE_RSP |
| Write Command | 修改,但不响应 | 无响应 |
11. 3.1 对 ATT Client 实现的要求
ATT Client 需要做两件事:
-
发现 handle;
-
基于 handle 访问 value。
11.1 Client 不能盲读
错误思路:
att_read(0x0007); // 假设 0x0007 一定是 Battery Level
正确思路:
1. Discover Primary Services
2. Find Battery Service handle range
3. Discover Characteristics in that range
4. Find Battery Level characteristic value handle
5. Read that handle
流程图:
11.2 Client 要处理同 type 多实例
Client 查找 UUID 时,要准备接收多个结果。
比如:
Find all characteristics with UUID = Temperature
可能返回多个 handle。
所以 Client 数据结构里应该能保存列表:
typedef struct {
uint16_t handle;
uint16_t end_handle;
att_uuid_t uuid;
} gatt_discovered_attr_t;
而不是只保存一个:
uint16_t temperature_handle;
当然,具体业务如果只关心第一个,那是上层策略,但 ATT/GATT discovery 层不能假设只有一个。
12. Protocol Requirements 与 GATT 的关系
3.1 虽然讲的是 ATT,但它实际上是 GATT 的底座。
GATT 中这些概念都能落到 ATT:
| GATT 概念 | ATT 基础 |
|---|---|
| Service | 一组 Attribute |
| Characteristic Declaration | Attribute |
| Characteristic Value | Attribute |
| Descriptor | Attribute |
| Characteristic UUID | Attribute Type |
| Characteristic Value Handle | Attribute Handle |
| Read Characteristic | 通过 handle 读 attribute value |
| Write Characteristic | 通过 handle 写 attribute value |
| CCCD | 一个有权限和 per-client value 的 descriptor attribute |
图示:
所以后面学 GATT 时,如果你已经掌握了 ATT 的 type、handle、value、permissions,很多概念会自然顺起来。
否则 GATT 里的 Service、Characteristic、Descriptor 会像一堆套娃,看着都熟,拆开就乱。
13. 抓包视角:3.1 怎么落地?
3.1 不是空谈,它直接体现在抓包里。
13.1 发现 handle
抓包中可能看到:
ATT_READ_BY_GROUP_TYPE_REQ
意思是:
Client 想发现某类 group attribute,比如 Primary Service。
返回:
ATT_READ_BY_GROUP_TYPE_RSP
里面会包含:
start handle
end group handle
attribute value
13.2 按 handle 读取 value
抓包中看到:
ATT_READ_REQ
Attribute Handle: 0x0007
意思是:
Client 想读取 handle 0x0007 对应 attribute 的 value。
返回:
ATT_READ_RSP
Attribute Value: ...
13.3 权限失败
如果 Server 不允许读,可能看到:
ATT_ERROR_RSP
Request Opcode In Error: ATT_READ_REQ
Attribute Handle In Error: 0x0007
Error Code: Read Not Permitted
如果需要加密但当前没加密,可能看到:
ATT_ERROR_RSP
Error Code: Insufficient Encryption
如果需要认证但当前安全等级不够,可能看到:
ATT_ERROR_RSP
Error Code: Insufficient Authentication
所以抓包时,看到 ATT_ERROR_RSP 不要立刻说“协议栈挂了”。
很多时候它只是 Server 在认真执行权限检查。
14. 3.1 的工程检查清单
实现 ATT Server 时,可以根据 3.1 做一个 checklist。
14.1 Attribute Database 检查
| 检查项 | 是否必须 |
|---|---|
| 每个 attribute 是否有 type | 是 |
| 每个 attribute 是否有 handle | 是 |
| 每个 attribute 是否有 value | 是 |
| 每个 attribute 是否有 permissions | 是 |
| handle 是否唯一 | 后续 3.2.2 会明确要求 |
| 是否支持按 handle 查找 | 是 |
| 是否支持按 type 查找 | discovery 需要 |
| 是否支持权限检查 | 是 |
| 是否支持加密状态检查 | 有安全 attribute 时必须 |
| 是否支持同 type 多实例 | 是 |
14.2 ATT Client 检查
| 检查项 | 说明 |
|---|---|
| 是否先 discovery 再访问 | 不应盲目写死 handle |
| 是否能处理多个相同 UUID | 同 type 可以出现多次 |
| 是否能处理权限错误 | Error Response 是正常协议结果 |
| 是否能处理 handle 变化 | GATT cache 可能失效 |
| 是否能处理加密后重试 | 安全不足时可能需要 pairing/encryption |
| 是否区分 UUID 和 handle | UUID 用于识别,handle 用于访问 |
15. 这一节里的几个“shall / may”工程含义
虽然 3.1 文字不多,但里面有几个工程上要特别注意的规范语义。
| 规范语义 | 工程含义 |
|---|---|
| 每个 attribute 有 type | DB 里不能有没有类型的 attribute |
| 每个 attribute 有 handle | Client 访问必须依靠 handle |
| 每个 attribute 有 value | value 是读写对象 |
| 同 type attribute 可以多次存在 | discovery 不能只返回第一个 |
| permissions 控制读写 | 访问前必须检查权限 |
| attribute value 可要求加密链路 | 没加密不能直接返回敏感 value |
| 安全方面另见 Section 4 | 权限检查不能只看 read/write,还要看 security |
16. 常见误区
16.1 误区一:UUID 能直接用来读写
不对。
ATT 的普通读写是基于 handle 的。
UUID 通常用于发现阶段,handle 用于访问阶段。
正确流程:
UUID -> discovery -> handle -> read/write
不是:
UUID -> read/write
16.2 误区二:一个 UUID 只会出现一次
不对。
同一个 attribute type 可以在同一个 Server 上出现多次。
例如多个 Characteristic Declaration 都是 0x2803。
16.3 误区三:知道 handle 就一定能访问
不对。
handle 只是门牌号,不是万能钥匙。
还要看:
-
是否可读;
-
是否可写;
-
是否需要加密;
-
是否需要认证;
-
是否需要授权;
-
密钥长度是否足够。
16.4 误区四:ATT 会解释 value 的业务含义
不对。
ATT 只搬运 octets。
业务含义由 GATT、Service、Profile 或应用层定义。
16.5 误区五:权限错误就是异常 bug
不一定。
权限错误是 ATT 协议定义的正常响应。
比如读一个不可读 attribute,Server 返回 Read Not Permitted 是正确行为。
17. 本篇小结
本篇学习了 3 Protocol requirements / 3.1 Introduction,核心结论如下:
-
每个 attribute 都有 attribute type。
-
Attribute type 通过 UUID 标识 attribute 代表什么。
-
每个 attribute 都有 attribute handle。
-
Attribute handle 用于在 Server 上访问 attribute。
-
每个 attribute 都有 attribute value。
-
Attribute value 是真实被读写的数据。
-
Client 通过 ATT PDU 发现 handles。
-
Attribute value 通过 handle 访问。
-
同一种 attribute type 可以在 Server 上出现多次。
-
Attribute 有 permissions,用来控制是否可读、可写、是否需要加密链路。
-
ATT 本身不解释 value 的业务含义。
-
UUID 用于识别,handle 用于访问。
-
权限检查是 ATT Server 实现的核心逻辑。
-
安全相关内容会在 Section 4 进一步定义。
-
GATT 的 Service、Characteristic、Descriptor 都建立在 ATT attribute 模型之上。
可以用一句话总结这一篇:
ATT 的世界里,UUID 负责“认人”,handle 负责“找门”,value 是“房间里的东西”,permissions 是“门禁和保安”。
下一篇继续学习:
第 4 篇:Attribute Type——UUID 是 Attribute 的身份证
下一篇会重点进入:
-
3.2.1 Attribute type;
-
UUID 的作用;
-
16-bit / 32-bit / 128-bit UUID;
-
SIG-defined UUID 与 vendor-specific UUID;
-
32-bit Attribute UUID 为什么要转换成 128-bit;
-
抓包里 UUID 字节序怎么看;
-
工程实现中 UUID 比较函数怎么设计。
更多推荐



所有评论(0)