GATTGeneric Attribute Profile)是 Bluetooth 中建立在 ATT 之上的服务框架,用 GATT ServiceGATT CharacteristicGATT Descriptor 把设备的状态、能力和控制入口组织成可发现、可读写、可通知的数据模型。

核心问题

两个 BLE 设备建立连接后,低层链路只负责传字节,应用仍然需要回答:

  • 对端有哪些能力?
  • 哪些数据可以读?
  • 哪些状态可以写入控制?
  • 哪些数据变化时可以主动推送?
  • 字节数组应该按什么语义解释?

GATT 解决的是“设备如何把能力暴露成标准化数据表,并让对端按统一过程发现和访问”的问题。它让心率带、温度计、门锁、传感器和自定义设备都能用同一种发现、读、写、通知流程组织数据。

核心对象

对象含义工程关注点
GATT Server持有 attribute database 的一方暴露服务、维护状态、检查权限
GATT Client发起发现、读写和订阅的一方缓存 handle、处理断连、订阅通知
ATT AttributeGATT 的底层存储单元handle、UUID、value、permissions
GATT Service一组相关特征的上下文primary/secondary、服务 UUID、handle range
GATT Characteristic一个可读写或可通知的数据项properties、value handle、value UUID
GATT Descriptor特征的元数据或配置项CCCD、展示格式、扩展属性
Bluetooth UUID标识 service/characteristic/descriptor 的类型16-bit SIG UUID 或 128-bit vendor UUID

GATT client/server 和 GAP central/peripheral 不是同一组角色。常见手机是 GAP central + GATT client,传感器是 GAP peripheral + GATT server;但设备可以同时实现 client 和 server。

核心机制

1. Server 先构造 attribute table

GATT 的真实底层是 ATT attribute table。一个简化的电池服务可以表示为:

handle  type    value
0x0001  0x2800  0x180F              # Primary Service: Battery Service
0x0002  0x2803  props=read|notify,
                 value_handle=0x0003,
                 uuid=0x2A19        # Characteristic declaration
0x0003  0x2A19  85                  # Battery Level value
0x0004  0x2902  0x0000              # CCCD: notification disabled

这里的 handle 是 server 内部给每个 attribute 分配的索引;type 是 attribute 自己的类型;value 是该 attribute 的值。GATT 用特定 UUID 解释这些 attribute:0x2800 表示 Primary Service,0x2803 表示 Characteristic Declaration,0x2902 表示 Client Characteristic Configuration Descriptor。

2. Client 通过 ATT 程序发现结构

典型 discovery 流程是:

  1. client 用 ATT discovery 请求查找 Primary Service。
  2. server 返回服务的 handle range 和 service UUID。
  3. client 在某个 service 的 handle range 内查找 characteristic declarations。
  4. client 得到 characteristic properties、value handle 和 characteristic UUID。
  5. client 根据 properties 和 permissions 选择 read、write、notify 或 indicate。
  6. 如果要接收 notification/indication,client 通常写对应 characteristic 后面的 CCCD descriptor。

这解释了为什么 BLE 调试工具经常显示 Service -> Characteristic -> Descriptor 树,但抓包里看到的是 ATT PDU 和 handle。GATT 是语义层,ATT 是实际访问 attribute table 的协议层。

3. 操作语义由 properties 和 permissions 共同决定

Characteristic properties 表示“这个 characteristic 在协议上允许哪些 GATT 过程”,例如 read、write、notify、indicate。Attribute permissions 则表示“访问该值需要什么权限和安全条件”,例如是否可读、是否可写、是否要求加密或认证。

因此:

allowed = characteristic_properties permit operation
trusted = attribute_permissions satisfied by current link security
actual access = allowed AND trusted

这不是数学公式,而是工程判断规则:properties 只说明操作类型是否存在,permissions 才决定当前连接是否真的能访问。一个 characteristic 可以声明支持 write,但未配对或未加密时仍返回权限相关错误。

4. Notification 和 Indication 用于状态推送

GATT 有两种常见主动上报方式:

  • Notification:server 主动发 characteristic value,不要求 client 用 ATT 响应确认。
  • Indication:server 主动发 characteristic value,client 必须返回 confirmation,可靠性更强但吞吐更低。

两者通常都要 client 先写 CCCD 订阅。没有写 CCCD 时,server 即使本地状态变化也不应把该 characteristic 推给这个 client。

工程用途

  • 传感器数据:电池电量、温度、心率、IMU 数据、设备状态。
  • 设备控制:写入开关、模式、阈值、Wi-Fi 配网参数或命令 characteristic。
  • 设备识别:读取 Device Information Service、Firmware Revision、Manufacturer Name。
  • 边缘设备调试:用手机 BLE 工具或 BlueZ 工具发现服务、读写 handle、订阅通知。
  • 自定义协议:用 vendor-specific service UUID + 自定义 characteristic 承载小数据控制面。

调试 GATT 时,重点看连接是否建立、service discovery 是否完整、handle cache 是否过期、characteristic properties 是否包含目标操作、CCCD 是否写成功、ATT_MTU 是否足够、链路是否满足安全权限。

边界与常见坑

  • GATT 不是广播协议:广播和发现主要由 GAP 管;GATT 典型工作在连接建立后的 attribute table 访问。
  • GATT 不是任意大数据通道:单个 ATT attribute value 有长度限制,大数据传输要考虑 ATT_MTU、分片、吞吐和重传策略。
  • handle 不是稳定 API:handle 是 server 本地表索引,固件升级或服务变化后可能改变;客户端应按 UUID 发现并处理 Service Changed。
  • UUID 不是实例 IDBluetooth UUID 标识类型,不标识某个设备上的某个实例;同一 service UUID 可以出现多次。
  • properties 不等于权限:read/write/notify bit 只是能力声明,实际访问还受 attribute permissions 和链路安全影响。
  • notification 不等于可靠交付到应用层:底层链路可能确认包到达协议栈,但应用是否处理成功是另一回事。
  • 不要混淆 GAP 与 GATT 角色:central/peripheral 管连接建立,client/server 管数据访问方向。
  • 缓存要处理 Service Changed:客户端缓存 services 和 handles 后,如果 server 数据库变化而不通知,可能读写到错误 handle。

相关术语

外部参考