4. Attribute Protocol (ATT):ATT PDU格式、Handle分配机制、MTU协商流程
好,咱们进入第四章节。Attribute Protocol,简称 ATT。这玩意儿是 GATT 的底层通信协议。说白了,GATT 定义的是「数据长什么样、怎么组织」,而 ATT 定义的是「这些数据怎么在设备之间传过去」。
我刚开始接触 BLE 时,总把 ATT 和 GATT 混为一谈。后来踩了几次坑才明白——GATT 是建筑蓝图,ATT 是搬运工。没有 ATT,你定义再漂亮的 Profile 也传不过去。
4.1 ATT PDU 格式
ATT 的通信单元叫 PDU(Protocol Data Unit)。每个 PDU 的结构非常简洁,就三个部分:操作码、参数、认证签名(可选)。
| 字段 | 长度 | 说明 |
|---|---|---|
| Opcode(操作码) | 1 字节 | 标识 PDU 类型(读、写、通知等) |
| Parameter(参数) | 可变(0~MTU-3) | 具体数据内容 |
| Authentication Signature(可选) | 12 字节 | 用于签名验证,非必选 |
操作码里藏着不少信息。它的高两位决定了 PDU 的「方法类型」:
- 0x00~0x3F:请求(Request)—— 客户端发,需要响应
- 0x40~0x7F:响应(Response)—— 服务器回给请求的
- 0x80~0xBF:命令(Command)—— 发出去不需要响应
- 0xC0~0xFF:通知/指示(Notification/Indication)—— 服务器主动推数据
举个例子,最常用的读请求 PDU:
Opcode: 0x0A (Read Request)
Parameters: 2 字节 Handle (小端序)
服务器收到后,回复 Read Response:
Opcode: 0x0B (Read Response)
Parameters: 可变长度 Value
嗯,这里要注意:ATT PDU 的最大长度受 MTU 限制。后面我们会详细讲 MTU 协商,先记住这个结论就行。
4.2 Handle 分配机制
每个 Attribute 都有一个唯一的 16 位 Handle,范围从 0x0001 到 0xFFFF。Handle 就是属性的「门牌号」,客户端通过 Handle 来读写属性。
Handle 的分配有讲究。我见过不少新手直接把 Handle 乱写一通,结果设备连上后读不到数据。其实规范里有一套约定俗成的规则:
- 0x0001:通常分配给 Primary Service
- 后续 Handle:按声明顺序递增,每个属性占一个 Handle
- 0xFFFF:保留,不用
举个例子,一个简单的设备服务:
| Handle | 类型 | UUID | 值 |
|---|---|---|---|
| 0x0001 | Primary Service | 0x1800 | Generic Access |
| 0x0002 | Characteristic | 0x2A00 | Device Name |
| 0x0003 | Characteristic Value | 0x2A00 | "MyDevice" |
| 0x0004 | Characteristic Descriptor | 0x2902 | 0x0000 |
Handle 还有一个重要特性:连续 Handle 可以组成一个 Group。比如一个 Service 包含多个 Characteristic,它们的 Handle 是连续的。客户端可以通过 Read By Group Type 请求,一次性读取整个 Service 的 Handle 范围。
4.3 MTU 协商流程
MTU(Maximum Transmission Unit)决定了 ATT PDU 的最大长度。默认 MTU 是 23 字节(3 字节 ATT 头 + 20 字节有效数据)。但你可以通过协商,把 MTU 增大到最高 512 字节。
为什么需要协商?你想想看,如果传输一个 100 字节的数据,默认 MTU 要拆成 5 个包,效率很低。增大 MTU 后,一个包就能搞定。
协商流程很简单,就两步:
- 客户端发起:发送 Exchange MTU Request,带上自己支持的 MTU 值
- 服务器响应:回复 Exchange MTU Response,带上自己支持的 MTU 值
最终使用的 MTU = min(客户端 MTU, 服务器 MTU)。
举个例子:
客户端 MTU = 247
服务器 MTU = 512
最终 MTU = min(247, 512) = 247
关键点:MTU 协商必须在 GATT 发现服务之前完成。如果先发现服务再协商 MTU,某些协议栈会忽略协商结果。我习惯在连接建立后的第一个 ATT 操作就做 MTU 协商。
下面这张图展示了完整的 MTU 协商流程:
最后提一句,MTU 协商不是强制的。如果你不协商,双方就用默认的 23 字节。但说实话,现在几乎没有设备还用默认 MTU 了,太慢了。我建议你每次连接都主动协商一次,花不了多少时间,但能显著提升体验。