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

看到后缀,基本就能判断:

  1. 谁发给谁;

  2. 要不要回应;

  3. 是否属于一个 transaction;

  4. 应该由 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

ATT Server ATT Client ATT Server ATT Client Command,CMD,不要回复 Request,REQ,需要回复 Response,RSP Notification,NTF,不要确认 Indication,IND,需要确认 Confirmation,CFM

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 流程

ATT Server ATT Client ATT Server ATT Client Server 不发送 ATT_WRITE_RSP ATT_WRITE_CMD(handle, value)

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 正常流程

ATT Server ATT Client ATT Server ATT Client ATT_READ_REQ(handle) ATT_READ_RSP(value)

4.3 Request 错误流程

ATT Server ATT Client ATT Server ATT Client ATT_READ_REQ(handle) ATT_ERROR_RSP(Request Opcode In Error, Handle In Error, Error Code)

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 处理状态机

Client sends REQ

Receive expected RSP

Receive ATT_ERROR_RSP

Transaction timeout

Idle

RequestSent

WaitingResponse

Complete

Error

Timeout

FailedBearer

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 流程

ATT Server ATT Client ATT Server ATT Client Client 不发送 ATT_HANDLE_VALUE_CFM ATT_HANDLE_VALUE_NTF(handle, value)

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 流程

ATT Server ATT Client ATT Server ATT Client ATT_HANDLE_VALUE_IND(handle, value) ATT_HANDLE_VALUE_CFM

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 流程状态机

Client receives IND

Send CFM

Idle

IndicationReceived

SendConfirmation

Server 侧:

Server sends IND

Receive CFM

30s timeout

Idle

IndicationSent

WaitingConfirmation

Timeout

FailedBearer


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/RSPATT_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 流程

ATT Server ATT Client ATT Server ATT Client Unsupported ATT_XXX_REQ ATT_ERROR_RSP Error Code = Request Not Supported Handle In Error = 0x0000

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 流程

ATT Server ATT Client ATT Server ATT Client Server ignores command Unsupported ATT_XXX_CMD

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。

流程:

ATT Server ATT Client ATT Server ATT Client ATT_READ_REQ with wrong length ATT_ERROR_RSP Error Code = Invalid PDU Handle In Error = 0x0000

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 流程

ATT Server ATT Client ATT Server ATT Client Server resource exhausted ATT_XXX_REQ ATT_ERROR_RSP Error Code = Insufficient Resources

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 处理逻辑

Send ATT_READ_REQ

Receive ATT_READ_RSP

Receive ATT_ERROR_RSP

No response

Idle

WaitingResponse

Success

Error

Timeout

FailedBearer

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 分发图

CMD / REQ / CFM

RSP / NTF / IND

Received ATT PDU

PDU suffix / type

ATT Server Engine

ATT Client Engine

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

Server Client Server Client One transaction ATT_XXX_REQ ATT_XXX_RSP or ATT_ERROR_RSP

17.2 Indication / Confirmation transaction

Client Server Client Server One transaction ATT_HANDLE_VALUE_IND ATT_HANDLE_VALUE_CFM

17.3 Command 和 Notification

Server Client Server Client No response transaction No confirmation transaction ATT_WRITE_CMD ATT_HANDLE_VALUE_NTF

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 处理框架

REQ

No

Yes

No

Yes

No

Yes

CMD

No

Yes

Receive ATT PDU

PDU Type

Request supported?

Command supported?

PDU valid?

Resource enough?

Process request

Send success response

Send ATT_ERROR_RSP

Ignore command

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 SupportedInvalid PDUInsufficient ResourcesUnlikely Error 都在本节被作为基础兜底处理规则提到。

20.1 错误响应总图

No

Yes

No

Yes

No

Yes

No

Yes

Received Request

Supported?

Valid PDU?

Resource enough?

Internal processing OK?

Send Success RSP

ATT_ERROR_RSP
Request Not Supported

ATT_ERROR_RSP
Invalid PDU

ATT_ERROR_RSP
Insufficient Resources

ATT_ERROR_RSP
Unlikely Error


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 总览

Attribute PDU

Command
CMD
Client to Server
No Response

Request
REQ
Client to Server
Needs Response

Response
RSP
Server to Client
Reply to Request

Notification
NTF
Server to Client
No Confirmation

Indication
IND
Server to Client
Needs Confirmation

Confirmation
CFM
Client to Server
Confirm Indication

25.2 Request / Response

ATT Server ATT Client ATT Server ATT Client alt [Success] [Error] ATT_XXX_REQ ATT_XXX_RSP ATT_ERROR_RSP

25.3 Indication / Confirmation

ATT Client ATT Server ATT Client ATT Server ATT_HANDLE_VALUE_IND ATT_HANDLE_VALUE_CFM

25.4 Command / Notification

ATT Server ATT Client ATT Server ATT Client No ATT response No ATT confirmation ATT_WRITE_CMD ATT_HANDLE_VALUE_NTF

25.5 Server 兜底错误处理

Yes

No

Yes

No

Yes

No

Yes

No

Received PDU

Request?

Command?

Supported?

Valid?

ATT_ERROR_RSP

Ignore

Process


26. 本篇小结

本篇学习了:

3.3 Attribute PDU

核心结论如下:

  1. Attribute PDU 有六种类型。

  2. 六种类型通过 PDU 名称后缀区分。

  3. CMD 是 Command,Client 发给 Server,不触发 Response。

  4. REQ 是 Request,Client 发给 Server,触发 Response。

  5. RSP 是 Response,Server 发给 Client,用于响应 Request。

  6. NTF 是 Notification,Server 主动发给 Client,不触发 Confirmation。

  7. IND 是 Indication,Server 主动发给 Client,触发 Confirmation。

  8. CFM 是 Confirmation,Client 发给 Server,用于确认 Indication。

  9. Server 至少必须支持并正确响应 ATT_FIND_INFORMATION_REQATT_READ_REQ

  10. 其他 PDU 类型是否必须支持,可由 higher layer specification 指定。

  11. Client 发送 request 后,必须支持该 request 的所有可能 response。

  12. Server 收到不支持的 request,应返回 ATT_ERROR_RSP,错误码 Request Not Supported (0x06)

  13. Server 收到不支持的 command,应忽略。

  14. Server 收到 invalid request,例如长度错误,应返回 ATT_ERROR_RSP,错误码 Invalid PDU (0x04)

  15. Server 资源不足时,应返回 ATT_ERROR_RSP,错误码 Insufficient Resources (0x11)

  16. Server 处理 request 中遇到内部错误时,可返回 Unlikely Error (0x0E)

  17. Notification 在 Unenhanced ATT bearer 上可能因 buffer overflow 被丢弃,所以要视为不可靠。

  18. Enhanced ATT bearer 上收到的 notification 必须被处理。

  19. 抓包时看到后缀,就要知道后面该不该有 response 或 confirmation。

  20. 协议栈实现中,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 为什么不能继续使用。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐