【Bluetooth-SIG】【CoreV6.2】【Vol3 Part F】【十一】【Attribute PDU 总览——六大类型】
Core_v6.2 Vol 3 Part F 学习笔记(十一):Attribute PDU 总览——六大类型
0. 本篇说明
本文继续学习 Bluetooth Core Specification v6.2:
Vol 3, Part F:Attribute Protocol,简称 ATT
本篇对应章节:
3.3 Attribute PDU
上一篇我们学习了:
-
ATT Bearer 是什么;
-
ATT PDU 通过 L2CAP channel 承载;
-
fixed ATT bearer 和 dynamic ATT bearer 的区别;
-
Enhanced ATT bearer / EATT 的基本概念;
-
收到 PDU 后,应该根据 PDU type 分发给 ATT Client 或 ATT Server;
-
一个 transaction 不能跨 ATT bearer 乱飞。
这一篇正式进入 Attribute PDU 的总览。
一句话先打底:
Attribute PDU 是 ATT 协议真正在线上跑的“消息包”。它一共分成六大类:Command、Request、Response、Notification、Indication、Confirmation。
通俗一点:
ATT PDU 就像蓝牙属性世界里的“对话句型”:
REQ是“我问你”;RSP是“我回答你”;CMD是“我通知你但你不用回”;NTF是“Server 喊一声”;IND是“Server 喊一声但你必须签收”;CFM是“我已签收”。
这六类分清楚,后面学所有 ATT opcode 都会顺很多。
分不清的话,看抓包就像看群聊:每句话都认识,但不知道谁该回谁。
1. Attribute PDU 是什么?
1.1 规范核心意思
规范说:
Attribute PDUs have one of six types, which are indicated by the suffix to the PDU name.
翻译:
Attribute PDU 有六种类型,这些类型通过 PDU 名称后缀表示。
例如:
ATT_READ_REQ
ATT_READ_RSP
ATT_WRITE_CMD
ATT_HANDLE_VALUE_NTF
ATT_HANDLE_VALUE_IND
ATT_HANDLE_VALUE_CFM
这些名字里的后缀非常关键:
| 后缀 | 类型 |
|---|---|
CMD |
Command |
REQ |
Request |
RSP |
Response |
NTF |
Notification |
IND |
Indication |
CFM |
Confirmation |
看到后缀,基本就能判断:
-
谁发给谁;
-
要不要回应;
-
是否属于一个 transaction;
-
应该由 ATT Client 处理,还是 ATT Server 处理。
2. Attribute PDU 六大类型总表
根据规范 Table 3.1,可以整理成下面这张表。
| 类型 | 后缀 | 方向 | 是否触发响应 / 确认 | 规范含义 | 通俗理解 |
|---|---|---|---|---|---|
| Command | CMD |
Client → Server | 不触发 Response | Client 发给 Server,不要求 Server 响应 | “我发给你,不用回” |
| Request | REQ |
Client → Server | 触发 Response | Client 发给 Server,要求 Server 响应 | “我问你,你得答” |
| Response | RSP |
Server → Client | 对 Request 的响应 | Server 对 Request 的回复 | “这是答案” |
| Notification | NTF |
Server → Client | 不触发 Confirmation | Server 主动发给 Client,不要求确认 | “我通知你一声” |
| Indication | IND |
Server → Client | 触发 Confirmation | Server 主动发给 Client,要求确认 | “重要通知,签收一下” |
| Confirmation | CFM |
Client → Server | 对 Indication 的确认 | Client 确认收到 Indication | “收到,别催了” |
2.1 一张图理解六类 PDU
2.2 一句话记忆
CMD:Client 发命令,不等回复
REQ:Client 发请求,必须有回应
RSP:Server 回答请求
NTF:Server 主动通知,不要签收
IND:Server 主动指示,必须签收
CFM:Client 签收 Indication
这几个后缀是 ATT 抓包识别的“路标”。
如果你看到 ATT_HANDLE_VALUE_IND 却不去找 ATT_HANDLE_VALUE_CFM,就像快递员让你签收,你转身就走,场面会有点尴尬。
3. Command:发出去,不等回复
3.1 Command 的定义
Command 是:
Client 发给 Server,但不触发 Response 的 PDU。
典型例子:
ATT_WRITE_CMD
ATT_SIGNED_WRITE_CMD
3.2 Command 流程
3.3 Command 的工程特点
| 特点 | 说明 |
|---|---|
| 无 ATT Response | 发完不等 RSP |
| 无普通 ATT Error Response | 命令类 PDU 不产生 ATT_ERROR_RSP |
| 适合高频写 | 比 Write Request 少一次往返 |
| 失败反馈弱 | 如果需要失败反馈,要靠上层机制 |
| 常用于 Write Without Response | 比如 OTA data chunk、日志配置、实时控制 |
规范还明确说:如果 Server 收到一个不支持的 command,并且 PDU 的 Command Flag 为 1,那么 Server 应该忽略它。
3.4 Command 类 PDU 的常见误区
错误想法:
我发了 ATT_WRITE_CMD,为什么没有 ATT_WRITE_RSP?
正确理解:
因为它是 CMD,设计上就没有 RSP。
如果你需要确认 Server 是否真的处理了这个写入,应该使用:
ATT_WRITE_REQ / ATT_WRITE_RSP
或者上层自己设计 ACK,比如:
Write Command -> Server Notification / Indication 回结果
3.5 类比
Command 像你把文件放到同事桌上,留张纸条:
“不用回我。”
但同事有没有看、有没有处理、有没有扔进垃圾桶,ATT 层不管。
如果你要确认,那就别用“放桌上”模式,要当面问。
4. Request:发出去,必须回答
4.1 Request 的定义
Request 是:
Client 发给 Server,并触发 Response 的 PDU。
典型例子:
ATT_READ_REQ
ATT_WRITE_REQ
ATT_FIND_INFORMATION_REQ
ATT_READ_BY_TYPE_REQ
ATT_PREPARE_WRITE_REQ
4.2 Request 正常流程
4.3 Request 错误流程
4.4 Request 的工程特点
| 特点 | 说明 |
|---|---|
| 必须有结果 | 成功返回对应 Response,失败返回 ATT_ERROR_RSP |
| 有 transaction | Request + Response 是一个事务 |
| Client 要维护 pending request | 发出 request 后等待 response |
| Server 必须处理或返回错误 | 不能装死 |
| 适合可靠配置和读取 | 读值、写配置、发现服务等 |
规范要求:如果 Client 发送一个 request,那么 Client 必须支持该 request 可能对应的所有 response PDUs。
这句话的工程含义是:
你既然敢问,就要准备好听到各种答案,包括错误答案。
比如你发 ATT_READ_REQ,就不能只处理 ATT_READ_RSP,还要能处理:
ATT_ERROR_RSP
否则 Server 认真报错,你这边却一脸茫然。
5. Response:Server 对 Request 的回答
5.1 Response 的定义
Response 是:
Server 发给 Client,用于响应 Client request 的 PDU。
典型例子:
ATT_READ_RSP
ATT_WRITE_RSP
ATT_FIND_INFORMATION_RSP
ATT_EXCHANGE_MTU_RSP
ATT_PREPARE_WRITE_RSP
ATT_EXECUTE_WRITE_RSP
5.2 Response 不能凭空出现
Response 应该对应一个之前的 Request。
例如:
ATT_READ_REQ -> ATT_READ_RSP
ATT_WRITE_REQ -> ATT_WRITE_RSP
如果抓包中突然冒出来:
ATT_READ_RSP
但前面没有对应的:
ATT_READ_REQ
那就要怀疑:
| 可能原因 | 说明 |
|---|---|
| 抓包缺包 | 前面的 request 没抓到 |
| bearer 匹配错了 | response 可能属于另一个 ATT bearer |
| 协议栈状态乱了 | pending request 管理异常 |
| 解码错误 | L2CAP CID 或 ATT opcode 解析错了 |
5.3 Response 处理状态机
Response 是 Client 侧事务闭环的关键。
Request 发出去,Response 回来,才算“这笔账结了”。
6. Notification:Server 主动通知,不需要确认
6.1 Notification 的定义
Notification 是:
Server 主动发给 Client,不触发 Confirmation 的 PDU。
典型例子:
ATT_HANDLE_VALUE_NTF
ATT_MULTIPLE_HANDLE_VALUE_NTF
6.2 Notification 流程
6.3 Notification 的工程特点
| 特点 | 说明 |
|---|---|
| Server 主动发送 | 不需要 Client 先发 read |
| 不需要 ATT Confirmation | ATT 层不等回执 |
| 适合高频数据 | 传感器、日志、进度、流式数据 |
| 通常受 CCCD 控制 | Client 开启后 Server 才发 |
| ATT 层无确认语义 | 业务需要可靠性时要另做机制 |
6.4 Notification 和 ATT Bearer 的可靠性差异
规范说明:在 Unenhanced ATT bearer 上,如果收到 notification 但因为 buffer overflow 无法处理,那么这些 notification 会被丢弃,因此应视为不可靠;而在 Enhanced ATT bearer 上,收到的 notification 必须总是被处理。
整理成表:
| Bearer 类型 | Notification 处理语义 |
|---|---|
| Unenhanced ATT bearer | 如果因 buffer overflow 无法处理,可以丢弃,所以要视为不可靠 |
| Enhanced ATT bearer | 收到 notification 后必须处理 |
| ATT 层确认 | Notification 本身仍然没有 CFM |
注意:
Enhanced ATT bearer 改善了接收处理能力和 flow control 语义,但 Notification 仍然不是 Indication。
它没有ATT_HANDLE_VALUE_CFM。
6.5 类比
Notification 像群里广播:
“电量变成 86% 了。”
大家看到就看到,没看到 ATT 层也不逐个点名确认。
如果这是“明天放假”,你最好别用 Notification;如果是“温度每秒更新”,Notification 很合适。
7. Indication:Server 主动指示,需要确认
7.1 Indication 的定义
Indication 是:
Server 主动发给 Client,并触发 Confirmation 的 PDU。
典型例子:
ATT_HANDLE_VALUE_IND
7.2 Indication 流程
7.3 Indication 的工程特点
| 特点 | 说明 |
|---|---|
| Server 主动发送 | 不需要 Client 先 read |
| 需要 Client 确认 | Client 必须回 ATT_HANDLE_VALUE_CFM |
| 有 transaction | Indication + Confirmation 是一个事务 |
| 适合关键数据 | Service Changed、控制结果、重要状态 |
| 吞吐较低 | 每个 indication 要等 confirmation |
| Server 要维护 outstanding indication | 等确认期间不能乱发同类事务 |
7.4 Indication 和 Notification 对比
| 项 | Notification | Indication |
|---|---|---|
| PDU | ATT_HANDLE_VALUE_NTF |
ATT_HANDLE_VALUE_IND |
| 是否要求确认 | 否 | 是 |
| 确认 PDU | 无 | ATT_HANDLE_VALUE_CFM |
| 典型用途 | 高频数据 | 关键数据 |
| ATT 层可靠语义 | 弱 | 强一些 |
| 吞吐 | 高 | 低 |
| 事务超时 | 不涉及 confirmation transaction | 涉及 indication-confirmation transaction |
7.5 抓包判断
如果看到:
ATT_HANDLE_VALUE_IND
后面应该找:
ATT_HANDLE_VALUE_CFM
如果迟迟没有 CFM,要查:
| 方向 | 可能问题 |
|---|---|
| 空口 / 链路 | IND 没到 Client |
| Client ATT | 收到了但没处理 |
| Client GATT | 没有正确确认 indication |
| Server | outstanding indication 状态机超时 |
| Bearer | transaction timeout 后 bearer 不应继续使用 |
8. Confirmation:Client 对 Indication 的签收
8.1 Confirmation 的定义
Confirmation 是:
Client 发给 Server,用于确认收到 Indication 的 PDU。
典型例子:
ATT_HANDLE_VALUE_CFM
8.2 Confirmation 不是普通 Response
Confirmation 只用于确认 Indication,不是对 Request 的 Response。
| PDU | 它回应什么 |
|---|---|
ATT_READ_RSP |
回应 ATT_READ_REQ |
ATT_WRITE_RSP |
回应 ATT_WRITE_REQ |
ATT_HANDLE_VALUE_CFM |
回应 ATT_HANDLE_VALUE_IND |
不要把 CFM 当成 RSP。
它俩都是“回话”,但回的不是同一类话。
8.3 Confirmation 流程状态机
Server 侧:
9. Server 最低必须支持哪些 Request?
9.1 规范要求
规范明确要求:
Server 必须能够接收并正确响应以下 request PDUs:
ATT_FIND_INFORMATION_REQ
ATT_READ_REQ
也就是说,这两个是 ATT Server 的基础必修课。
9.2 为什么是这两个?
| PDU | 作用 | 为什么基础 |
|---|---|---|
ATT_FIND_INFORMATION_REQ |
获取 handle 与 type 的映射 | Client 用它发现 attribute 列表 |
ATT_READ_REQ |
按 handle 读取 attribute value | 最基本的数据读取能力 |
如果一个 ATT Server 连这两个都不支持,就像图书馆:
-
不告诉你书架上有什么书;
-
也不让你读书。
那这个 ATT Server 基本没法玩。
9.3 其他 PDU 谁决定是否支持?
规范说:
其他 PDU 类型是否需要支持,可以由 higher layer specification 指定。
也就是说,具体到 GATT、Service、Profile 时,可能会要求支持更多 PDU。
例如:
| 上层需求 | 需要的 ATT PDU |
|---|---|
| GATT Service Discovery | ATT_READ_BY_GROUP_TYPE_REQ/RSP |
| Characteristic Discovery | ATT_READ_BY_TYPE_REQ/RSP |
| Write Characteristic Value | ATT_WRITE_REQ/RSP |
| Write Without Response | ATT_WRITE_CMD |
| Notification | ATT_HANDLE_VALUE_NTF |
| Indication | ATT_HANDLE_VALUE_IND/CFM |
| Long Read | ATT_READ_BLOB_REQ/RSP |
| Long Write | ATT_PREPARE_WRITE_REQ/RSP + ATT_EXECUTE_WRITE_REQ/RSP |
GATT 的 procedure 到 ATT opcode 映射表也说明了很多 GATT procedure 会映射到这些 ATT PDU,例如写长特征值和可靠写会用 ATT_PREPARE_WRITE_REQ/RSP、ATT_EXECUTE_WRITE_REQ/RSP,Notification 用 ATT_HANDLE_VALUE_NTF,Indication 用 ATT_HANDLE_VALUE_IND/CFM。
10. 不支持 Request:必须回 ATT_ERROR_RSP
10.1 规范要求
如果 Server 收到一个不支持的 request,那么必须返回:
ATT_ERROR_RSP
Error Code = Request Not Supported (0x06)
Attribute Handle In Error = 0x0000
规范中明确说明,不支持的 request 要用 ATT_ERROR_RSP 返回 Request Not Supported (0x06),并将 Attribute Handle In Error 设为 0x0000。
10.2 流程
10.3 工程解释
Request 是“一问一答”模型。
即使 Server 不支持,也不能装死。
错误实现:
// 收到不支持的 request,直接 return
return;
正确实现:
att_send_error_rsp(bearer,
request_opcode,
0x0000,
ATT_ERR_REQUEST_NOT_SUPPORTED);
装死的后果是 Client 等到 timeout,然后这个 ATT bearer 进入失败状态。
这就像你问别人“会不会”,对方不说“不会”,而是盯着你不动 30 秒。协议栈会觉得这人不适合继续聊天。
11. 不支持 Command:忽略
11.1 规范要求
如果 Server 收到一个不支持的 command,并且 PDU 的 Command Flag 为 1,那么 Server 应该忽略该 command。
11.2 为什么和 Request 不一样?
因为 command 的定义就是:
不触发 Response。
所以 Server 不能回:
ATT_ERROR_RSP
否则就破坏了 command 的语义。
11.3 流程
11.4 工程解释
Command 像“单向投递”。
如果你不支持,就安静地丢弃。
不要回包,不要报错,不要自己加戏。
错误实现:
if (!supported_command) {
att_send_error_rsp(...); // 错,不该对 command 回 ATT_ERROR_RSP
}
正确实现:
if (!supported_command) {
return; // ignore
}
当然,业务上如果需要告知失败,那应该由上层协议另行设计结果通道,而不是让 ATT 层对 command 硬回 error。
12. Invalid Request:返回 Invalid PDU
12.1 规范要求
如果 Server 收到 invalid request,例如 PDU 长度错误,那么应返回:
ATT_ERROR_RSP
Error Code = Invalid PDU (0x04)
Attribute Handle In Error = 0x0000
规范中给出的例子就是“PDU wrong length”。
12.2 示例
比如 ATT_READ_REQ 正常格式应该是:
Opcode 1 octet + Attribute Handle 2 octets = 3 octets
如果收到:
0A 12
只有 2 octets,那就是 invalid PDU。
流程:
12.3 工程处理
if (opcode == ATT_READ_REQ && len != 3) {
att_send_error_rsp(bearer,
ATT_READ_REQ,
0x0000,
ATT_ERR_INVALID_PDU);
return;
}
12.4 注意
这里 Attribute Handle In Error = 0x0000 的原因是:
PDU 格式本身都不对,不一定能可靠解析出 handle。
不要硬从错误长度包里抠 handle。
这就像快递单被水泡烂了,地址看不清,你还非要说“我觉得是 301”,这很危险。
13. 资源不足:返回 Insufficient Resources
13.1 规范要求
如果 Server 没有足够资源处理 request,应返回:
ATT_ERROR_RSP
Error Code = Insufficient Resources (0x11)
Attribute Handle In Error = 0x0000
规范中明确要求资源不足时返回 Insufficient Resources (0x11)。
13.2 常见资源不足场景
| 场景 | 说明 |
|---|---|
| response buffer 不够 | 无法构造响应 |
| prepare write queue 满 | 后续具体 PDU 可能有专门错误码 |
| 内存不足 | 无法分配临时 buffer |
| long read snapshot 无法创建 | 无法保护一致性 |
| application callback busy | 上层无法处理 |
| 多 bearer 并发过高 | 状态资源被占满 |
13.3 流程
13.4 工程建议
资源不足不是“崩溃理由”,而是“协议结果”。
不要:
assert(buffer != NULL);
更稳:
if (buffer == NULL) {
att_send_error_rsp(bearer,
opcode,
0x0000,
ATT_ERR_INSUFFICIENT_RESOURCES);
return;
}
协议栈里 assert 用错地方,就是“遇到坏人我先自爆”。
设备在现场跑的时候,不能这么刚烈。
14. 处理过程出错:返回 Unlikely Error
14.1 规范要求
如果 Server 处理 request 的过程中遇到错误,无法处理该 request,则应返回:
ATT_ERROR_RSP
Error Code = Unlikely Error (0x0E)
Attribute Handle In Error = 0x0000
规范中把这种“处理过程中遇到错误”的情况归到 Unlikely Error (0x0E)。
14.2 Unlikely Error 是什么?
Unlikely Error 可以理解成:
按理说不该发生,但确实发生了的内部错误。
比如:
| 场景 | 说明 |
|---|---|
| attribute callback 返回内部失败 | 上层处理失败 |
| 数据库状态异常 | DB 结构不一致 |
| 存储读取失败 | NVM/Flash 读失败 |
| 内部状态机不允许 | 状态异常 |
| 资源看似可用但处理失败 | 中途失败 |
14.3 什么时候不要用 Unlikely Error?
不要把所有错误都丢给 Unlikely Error。
更具体的错误应该用具体错误码:
| 错误 | 应用错误码 |
|---|---|
| handle 无效 | Invalid Handle |
| 不允许读 | Read Not Permitted |
| 不允许写 | Write Not Permitted |
| 未加密 | Insufficient Encryption |
| 未认证 | Insufficient Authentication |
| value 长度非法 | Invalid Attribute Value Length |
| offset 过界 | Invalid Offset |
| request 不支持 | Request Not Supported |
Unlikely Error 是兜底,不是垃圾桶。
15. Client 发送 Request 后要支持所有可能 Response
15.1 规范要求
规范说:
If a client sends a request, then the client shall support all possible response PDUs for that request.
翻译:
如果 Client 发送某个 request,那么它必须支持该 request 的所有可能响应 PDU。
15.2 工程含义
发一个 request,可能有两类结果:
| 结果类型 | 示例 |
|---|---|
| 成功响应 | ATT_READ_RSP |
| 错误响应 | ATT_ERROR_RSP |
比如发:
ATT_READ_REQ
你必须处理:
ATT_READ_RSP
ATT_ERROR_RSP
不能只处理成功,不处理失败。
15.3 Client 处理逻辑
15.4 常见 bug
错误代码:
if (rsp_opcode == ATT_READ_RSP) {
handle_read_success();
}
漏了:
else if (rsp_opcode == ATT_ERROR_RSP) {
handle_read_error();
}
漏处理 ATT_ERROR_RSP 后,上层会误以为 request “没结果”。
实际上 Server 已经认真回答你:“不行,原因如下。”
你没听懂,不能怪 Server 高冷。
16. PDU 后缀与分发路径
16.1 分发规则
结合上一篇 ATT bearer 的内容:
| PDU 后缀 | 本地收到后交给谁 |
|---|---|
CMD |
ATT Server |
REQ |
ATT Server |
RSP |
ATT Client |
NTF |
ATT Client |
IND |
ATT Client |
CFM |
ATT Server |
16.2 分发图
16.3 代码示例
typedef enum {
ATT_PDU_COMMAND,
ATT_PDU_REQUEST,
ATT_PDU_RESPONSE,
ATT_PDU_NOTIFICATION,
ATT_PDU_INDICATION,
ATT_PDU_CONFIRMATION,
ATT_PDU_UNKNOWN,
} att_pdu_type_t;
void att_dispatch_pdu(att_connection_t *conn,
att_bearer_t *bearer,
const uint8_t *pdu,
uint16_t len)
{
uint8_t opcode = pdu[0];
att_pdu_type_t type = att_get_pdu_type(opcode);
switch (type) {
case ATT_PDU_COMMAND:
case ATT_PDU_REQUEST:
case ATT_PDU_CONFIRMATION:
att_server_handle_pdu(conn, bearer, pdu, len);
break;
case ATT_PDU_RESPONSE:
case ATT_PDU_NOTIFICATION:
case ATT_PDU_INDICATION:
att_client_handle_pdu(conn, bearer, pdu, len);
break;
default:
att_handle_unknown_pdu(conn, bearer, opcode);
break;
}
}
17. 六类 PDU 与 transaction 的关系
虽然下一篇才会详细讲 transaction,但这里可以先建立直觉。
| 类型 | 是否构成 transaction |
|---|---|
| Command | 通常不构成 request-response transaction |
| Request + Response | 构成一个 transaction |
| Notification | 不构成 indication-confirmation transaction |
| Indication + Confirmation | 构成一个 transaction |
17.1 Request / Response transaction
17.2 Indication / Confirmation transaction
17.3 Command 和 Notification
18. 抓包分析:看到后缀应该怎么想?
18.1 看到 REQ
例如:
ATT_READ_REQ
你应该立刻问:
后面有没有 ATT_READ_RSP 或 ATT_ERROR_RSP?
18.2 看到 RSP
例如:
ATT_READ_RSP
你应该回头找:
前面对应的 ATT_READ_REQ 是哪个?
在哪个 bearer / CID 上?
18.3 看到 CMD
例如:
ATT_WRITE_CMD
你不要找:
ATT_WRITE_RSP
因为没有。
18.4 看到 NTF
例如:
ATT_HANDLE_VALUE_NTF
你不要找:
ATT_HANDLE_VALUE_CFM
因为 Notification 不要确认。
18.5 看到 IND
例如:
ATT_HANDLE_VALUE_IND
你必须找:
ATT_HANDLE_VALUE_CFM
18.6 看到 CFM
例如:
ATT_HANDLE_VALUE_CFM
你要回头找:
对应的 ATT_HANDLE_VALUE_IND 是哪个?
19. ATT Server 最小能力模型
根据本节,最小 ATT Server 至少要支持:
ATT_FIND_INFORMATION_REQ
ATT_READ_REQ
并且要能对不支持的 request 返回 ATT_ERROR_RSP。
19.1 最小 Server 处理框架
19.2 伪代码
void att_server_handle_pdu(att_connection_t *conn,
att_bearer_t *bearer,
const uint8_t *pdu,
uint16_t len)
{
uint8_t opcode = pdu[0];
switch (opcode) {
case ATT_FIND_INFORMATION_REQ:
att_handle_find_information_req(conn, bearer, pdu, len);
break;
case ATT_READ_REQ:
att_handle_read_req(conn, bearer, pdu, len);
break;
case ATT_WRITE_CMD:
if (!att_server_supports_write_cmd(conn)) {
/* Unsupported command: ignore */
return;
}
att_handle_write_cmd(conn, bearer, pdu, len);
break;
default:
if (att_is_request(opcode)) {
att_send_error_rsp(bearer,
opcode,
0x0000,
ATT_ERR_REQUEST_NOT_SUPPORTED);
} else if (att_is_command(opcode)) {
/* Unsupported command: ignore */
return;
} else {
/* Unknown / malformed: policy depends on decoding context */
att_handle_unknown_or_invalid_pdu(conn, bearer, pdu, len);
}
break;
}
}
20. Error Handling 总览:本节先记四个兜底错误
这一篇不是正式讲 ATT_ERROR_RSP,但 3.3 已经涉及几个基本错误处理:
| 场景 | Response |
|---|---|
| Request 不支持 | ATT_ERROR_RSP, Request Not Supported (0x06), Handle In Error = 0x0000 |
| Command 不支持 | 忽略 |
| Request 格式无效,例如 PDU 长度错误 | ATT_ERROR_RSP, Invalid PDU (0x04), Handle In Error = 0x0000 |
| Server 资源不足 | ATT_ERROR_RSP, Insufficient Resources (0x11), Handle In Error = 0x0000 |
| 处理过程中遇到无法处理的内部错误 | ATT_ERROR_RSP, Unlikely Error (0x0E), Handle In Error = 0x0000 |
其中 Request Not Supported、Invalid PDU、Insufficient Resources、Unlikely Error 都在本节被作为基础兜底处理规则提到。
20.1 错误响应总图
21. Unenhanced / Enhanced Bearer 对 Notification 的影响
这一点在后续 sequential protocol 中会继续展开,但和 PDU 类型理解关系很大,可以先放在这里打底。
规范说明:Unenhanced ATT bearer 上,如果 notification 因 buffer overflow 无法处理,应丢弃,因此这些 PDU 要被视为不可靠;Enhanced ATT bearer 上,notification 收到后必须处理。
21.1 对比表
| PDU 类型 | Unenhanced ATT bearer | Enhanced ATT bearer |
|---|---|---|
| Notification | 可能因 buffer overflow 被丢弃,要视为不可靠 | 收到后必须处理 |
| Indication | 有 Confirmation 事务 | 有 Confirmation 事务 |
| Request | 有 Response 事务 | 有 Response 事务 |
| Command | 不触发 Response | 不触发 Response |
21.2 工程含义
如果你用 Notification 做关键业务结果,上层最好自己再设计可靠机制,例如:
| 方式 | 说明 |
|---|---|
| 改用 Indication | ATT 层有 CFM |
| Notification + 序列号 | Client 检测丢包 |
| Notification + 应用 ACK | 自定义确认 characteristic |
| EATT | 改善 flow control,但仍不等于 IND |
| 重传机制 | 上层协议实现 |
Notification 很像喊话,不是签收单。
喊话可以很快,但别拿它当法律合同。
22. 与 GATT Procedure 的关系
GATT 是 ATT 的上层。很多 GATT procedure 最终都落到这些 PDU 类型上。
| GATT Procedure | ATT PDU 类型 |
|---|---|
| Discover All Primary Services | Request / Response / Error |
| Discover Characteristics | Request / Response / Error |
| Read Characteristic Value | Request / Response / Error |
| Write Characteristic Value | Request / Response / Error |
| Write Without Response | Command |
| Notification | Notification |
| Indication | Indication / Confirmation |
| Reliable Write | Request / Response 组合 |
| Long Read | 多个 Request / Response transaction |
GATT procedure 到 ATT opcode 的映射表也说明:Write Without Response 映射到 ATT_WRITE_CMD,Signed Write Without Response 映射到 ATT_SIGNED_WRITE_CMD,Characteristic Value Notification 映射到 ATT_HANDLE_VALUE_NTF,Characteristic Value Indication 映射到 ATT_HANDLE_VALUE_IND/CFM。
23. 常见误区
23.1 误区一:所有 Client 发给 Server 的 PDU 都要 Response
不对。
CMD 不触发 Response。REQ 才触发 Response。
23.2 误区二:Server 不支持 Command 时应该回 Error
不对。
不支持的 command 应忽略。
不支持的 request 才返回 ATT_ERROR_RSP。
23.3 误区三:Notification 和 Indication 都只是 Server 主动发,差不多
不对。
Notification 不需要 confirmation。
Indication 需要 confirmation。
23.4 误区四:Confirmation 是普通 Response
不对。
Confirmation 只用于确认 Indication。
它不是对 Request 的 Response。
23.5 误区五:Client 只需要处理成功 Response
不对。
Client 发 request 后,必须支持所有可能响应,包括 ATT_ERROR_RSP。
23.6 误区六:Invalid PDU 可以直接丢
对于 invalid request,不应直接丢;应返回 ATT_ERROR_RSP,错误码为 Invalid PDU (0x04),Handle In Error 通常为 0x0000。
23.7 误区七:Notification 绝对可靠
不对。
Unenhanced ATT bearer 上,notification 因 buffer overflow 无法处理时会被丢弃。
24. 工程检查清单
24.1 ATT Server 检查
| 检查项 | 说明 |
|---|---|
是否支持 ATT_FIND_INFORMATION_REQ |
必须 |
是否支持 ATT_READ_REQ |
必须 |
不支持 request 是否返回 ATT_ERROR_RSP |
必须 |
Request Not Supported 时 handle 是否设为 0x0000 |
必须 |
| 不支持 command 是否 ignore | 必须 |
invalid request 是否返回 Invalid PDU |
必须 |
资源不足是否返回 Insufficient Resources |
必须 |
内部处理失败是否合理返回 Unlikely Error |
需要 |
| 是否根据 PDU type 分发给 Client / Server | 必须 |
24.2 ATT Client 检查
| 检查项 | 说明 |
|---|---|
| 发送 request 后是否等待 response | 必须 |
| 是否处理成功 response | 必须 |
是否处理 ATT_ERROR_RSP |
必须 |
| 是否处理 transaction timeout | 必须 |
| 收到 indication 是否发送 confirmation | 必须 |
| 收到 notification 是否不发送 confirmation | 必须 |
| 是否区分 command 和 request | 必须 |
| 是否按 bearer 维护 pending request | 多 bearer 必须 |
24.3 抓包检查
| 抓包现象 | 应检查 |
|---|---|
REQ 后没有 RSP/ERROR_RSP |
transaction 可能超时 |
CMD 后没有 response |
正常 |
IND 后没有 CFM |
Client 处理或链路可能有问题 |
NTF 后没有 CFM |
正常 |
| 不支持 request 后无 error | Server bug |
| 不支持 command 后无 response | 正常 |
| Invalid request 后无 error | Server bug |
| Notification 丢失 | 看 bearer 类型、buffer、flow control |
25. 本篇核心图
25.1 六类 PDU 总览
25.2 Request / Response
25.3 Indication / Confirmation
25.4 Command / Notification
25.5 Server 兜底错误处理
26. 本篇小结
本篇学习了:
3.3 Attribute PDU
核心结论如下:
-
Attribute PDU 有六种类型。
-
六种类型通过 PDU 名称后缀区分。
-
CMD是 Command,Client 发给 Server,不触发 Response。 -
REQ是 Request,Client 发给 Server,触发 Response。 -
RSP是 Response,Server 发给 Client,用于响应 Request。 -
NTF是 Notification,Server 主动发给 Client,不触发 Confirmation。 -
IND是 Indication,Server 主动发给 Client,触发 Confirmation。 -
CFM是 Confirmation,Client 发给 Server,用于确认 Indication。 -
Server 至少必须支持并正确响应
ATT_FIND_INFORMATION_REQ和ATT_READ_REQ。 -
其他 PDU 类型是否必须支持,可由 higher layer specification 指定。
-
Client 发送 request 后,必须支持该 request 的所有可能 response。
-
Server 收到不支持的 request,应返回
ATT_ERROR_RSP,错误码Request Not Supported (0x06)。 -
Server 收到不支持的 command,应忽略。
-
Server 收到 invalid request,例如长度错误,应返回
ATT_ERROR_RSP,错误码Invalid PDU (0x04)。 -
Server 资源不足时,应返回
ATT_ERROR_RSP,错误码Insufficient Resources (0x11)。 -
Server 处理 request 中遇到内部错误时,可返回
Unlikely Error (0x0E)。 -
Notification 在 Unenhanced ATT bearer 上可能因 buffer overflow 被丢弃,所以要视为不可靠。
-
Enhanced ATT bearer 上收到的 notification 必须被处理。
-
抓包时看到后缀,就要知道后面该不该有 response 或 confirmation。
-
协议栈实现中,PDU type 是分发到 ATT Client 还是 ATT Server 的关键。
一句话总结:
ATT PDU 六大类型就是 ATT 的“说话方式”:REQ 要答,CMD 不答;NTF 喊一声,IND 要签收;RSP 回请求,CFM 回指示。抓包先看后缀,方向和事务关系就清楚一半。
下一篇继续学习:
第 12 篇:Attribute PDU Format、Sequential Protocol、Transaction
下一篇重点:
-
3.3.1 Attribute PDU format;
-
Attribute Opcode;
-
Authentication Signature;
-
Command Flag;
-
Authentication Signature Flag;
-
Method;
-
Signed Write Command;
-
3.3.2 Sequential protocol;
-
一个 bearer 上 request/response 的顺序约束;
-
notification、command、indication 对 flow control 的影响;
-
3.3.3 Transaction;
-
30 秒 transaction timeout;
-
timeout 后 ATT bearer 为什么不能继续使用。
更多推荐

所有评论(0)