第2章 特征操作基础读/写/通知/指示:操作码、ATT PDU结构、MTU协商、流程时序
本章深入剖析GATT特征操作的最小执行单元——ATT协议栈。你将掌握四种核心操作的底层协议帧、MTU协商机制以及完整交互时序,为后续高层应用开发打下坚实的依赖边界控制基础。
本章学习目标
- 准确辨认4类操作码(Read/Write/Notify/Indicate)的ATT PDU结构
- 掌握MTU协商的客户端/服务端角色与报文交互流程
- 能画出操作时序图并识别典型异常分支(超时、重传、流控)
- 通过抓包验证MTU对通知/指示数据包长度的影响
2.1 ATT操作码与PDU结构
ATT(Attribute Protocol)定义在BLE 4.0+核心规范Vol.3 Part F。每个操作由客户端发起,服务端响应。操作码(Opcode)位于PDU首字节,决定了后续数据布局。
| 操作方向 | 操作码(Hex) | ATT PDU结构 | 对应GATT操作 |
|---|---|---|---|
| Client→Server | 0x0A | Opcode + Attribute Handle (2B) | Read Request |
| Server→Client | 0x0B | Opcode + Value (可变长) | Read Response |
| Client→Server | 0x12 | Opcode + Handle (2B) + Value (可变长) | Write Request / Write Command |
| Server→Client | 0x1B | Opcode + Handle (2B) + Value (可变长) | Notification / Indication |
| Server→Client | 0x1D | Opcode + Handle (2B) + Value (可变长) | Indication(需要确认) |
关键点:所有PDU的最大长度受当前ATT MTU约束,超出部分将被截断或分片(LL层处理)。
// ATT Read Request PDU 示例(十六进制)
0x0A // Opcode
0x00 0x17 // Attribute Handle = 0x0017
// ATT Write Request PDU 示例
0x12 // Opcode
0x00 0x2A // Handle = 0x002A
0x48 0x65 0x6C 0x6C 0x6F // Value = "Hello"
// ATT Notification PDU 示例(Server主动推送)
0x1B // Opcode
0x00 0x2A // Handle = 0x002A
0x54 0x65 0x6D 0x70 0x3A 0x32 0x35 0x2E 0x33 // Value = "Temp:25.3"
2.2 MTU协商机制
MTU(Maximum Transmission Unit)决定ATT层可承载的最大有效载荷。默认值为23字节(3字节头 + 20字节数据)。通过MTU Exchange Request/Response可以提升至最大512字节(BLE 4.2+支持)。
协商流程如下:
- 客户端发送
MTU Exchange Request(Opcode 0x02),携带客户端支持的MTU值(2字节Little Endian)。 - 服务端回复
MTU Exchange Response(Opcode 0x03),携带服务端支持的MTU值。 - 双方取 min(客户端MTU, 服务端MTU) 作为后续连接的ATT MTU。
| 阶段 | PDU示例(Hex) | 说明 |
|---|---|---|
| MTU Request(Client→Server) | 02 F4 01 |
Opcode=0x02, Client MTU=0x01F4=500 |
| MTU Response(Server→Client) | 03 30 01 |
Opcode=0x03, Server MTU=0x0130=304 |
| 协商结果 | min(500,304)=304 | 后续ATT有效载荷最大304字节 |
工程提示:MTU协商应在GATT Service Discovery之前完成。许多BLE协议栈(如ESP-IDF、Zephyr)默认在连接后立即自动发起协商。若应用需要大包传输(如OTA固件),请显式调用API提高MTU:
// Android (BluetoothGatt)
bluetoothGatt.requestMtu(512);
// iOS (CoreBluetooth)
peripheral.maximumWriteValueLength(for: .withoutResponse)
// 注意iOS不直接支持MTU协商,系统自动优化
// ESP-IDF
esp_ble_gattc_exchange_mtu(remote_bda, mtu_size);
2.3 四种操作的完整流程时序
以下SVG图展示一个典型GATT Read/Write/Notify/Indicate的交互时序,重点标注原子操作、确认与流控边界。
时序要点解析:
- 读操作:请求→响应,响应必须包含完整的Value(允许空值)。
- 写操作:Write Request需等待Write Response确认;Write Command(无响应)则直接丢弃,无反馈。
- 通知(Notify):单向推送,Client无需确认。流量控制依靠LL层信用和ATT层MTU,无应用层确认。
- 指示(Indicate):Server发送后,Client必须回复Handle Value Confirm(0x1E),否则Server不会发送下一条指示。这是关键流控边界。
- 超时处理:ATT响应超时通常为30秒(可配置)。超时后Client应发起连接终止或重传。
2.4 常见错误与调试方法
| 错误现象 | 可能原因 | 调试手段 |
|---|---|---|
| Read Response返回0长度 | 特征属性无可读权限,或服务端内部错误 | 检查CCC描述符配置;抓包确认Error PDU |
| Write Request无响应 | 特征权限为“Write Without Response” | 用Write Command代替;查看栈日志 |
| Notification/Indication未收到 | CCC描述符未使能(未写入0x0001或0x0002) | 使用BLE嗅探器检查CCC写操作 |
| 大包被截断 | ATT MTU未协商或被限制为23 | 调用MTU Request;检查对端能力 |
| Indication风暴导致App无响应 | 应用层未及时发送Confirm | 在Confirm回调中尽快处理;检查线程阻塞 |
性能与稳定性优化:
- MTU选择:并非越大越好。BLE 4.2 PHY层最多251字节有效数据,建议协商至247字节(头+数据匹配LL PDU)。过大的MTU会增加重传代价。
- Notification流控:多数协议栈内部有发送队列(如Zephyr的BT_GATT_NOTIFY_MULTI),无需应用层逐包确认。但若通知频率超过20ms/包,可能触发LL层信令拥塞。
- Indication并发限制:同一连接同一时刻只能有一个未确认的Indication。设计多特征推送时,若需要可靠性,考虑分时发送或使用多连接。
- 写操作顺序:Write Command无顺序保证,Write Request保证顺序。若需要可靠顺序写入(如配置寄存器),必须使用Write Request + Response。
2.5 实战抓包示例(基于nRF Sniffer)
以下为一次MTU协商 + Read操作的真实PDU序列(Wireshark输出格式):
Frame 1: Advertising (同连接建立)
Frame 2: LL_CONNECTION_UPDATE_REQ (已过滤)
Frame 3: ATT MTU Exchange Request (0x02 0x00 0x01) — Client MTU = 256
Frame 4: ATT MTU Exchange Response (0x03 0x00 0x01) — Server MTU = 256
Frame 5: ATT Read By Group Type Request (Service Discovery)
...
Frame 12: ATT Read Request (0x0A 0x00 0x17)
Frame 13: ATT Read Response (0x0B 0x41 0x42 0x43) — Value = "ABC"
Frame 14: ATT Write Request (0x12 0x00 0x2A 0x44 0x45)
Frame 15: ATT Write Response (0x13) — ACK
Frame 16: ATT Handle Value Notification (0x1B 0x00 0x2A 0x31 0x32)
Frame 17: (无确认)
Frame 18: ATT Handle Value Indication (0x1D 0x00 0x2A 0x33 0x34)
Frame 19: ATT Handle Value Confirm (0x1E) — 由Client自动回复
调试命令示例(host controller interface):
# 使用hcitool查看连接MTU(Linux BlueZ)
btmon -w trace.log # 实时捕获HCI日志
# 通过btgatt-cmd读取特征
bluetoothctl menu gatt
# 扫描后连接
# select-attribute /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service000a/char000b
# read
2.6 本章知识结构一览
| 模块 | 关键概念 | 依赖边界 |
|---|---|---|
| ATT PDU | Opcode、Handle、Value、错误码 | MTU限制PDU长度 |
| MTU协商 | Exchange Req/Resp、min策略 | 必须在数据传输前完成 |
| 读操作 | Read Req/Resp、Read Long | 数据>MTU时需分块读取 |
| 写操作 | Write Req/Resp、Write Cmd、Prepare Write | Write Cmd无流控,可能丢失 |
| 通知/指示 | Notif(0x1B)、Ind(0x1D)、Confirm(0x1E) | Ind必须收到Confirm才发下一条 |