第2章:特征读写属性与权限配置

2.1 概述

GATT 特征(Characteristic)的属性与权限配置是 BLE 服务端开发的核心环节,它决定了客户端能够以何种方式访问特征值,以及是否需要满足安全条件。本章详细讲解读写属性(Read / Write / Notify / Indicate)、安全权限(加密 / 配对)以及 CCCD(Client Characteristic Configuration Descriptor)配置的原理、实现流程与调试方法。

学习目标:掌握特征属性的声明语法,理解属性与权限的层级关系,能够独立完成 CCCD 的使能与同步,并排查常见配置错误。
特征属性与权限配置决策流程 特征属性与权限配置流程 ① 特征定义 UUID + 初始值 + 属性标志 ② 属性选择 Read / Write / Notify / Indicate 组合 ③ 安全权限配置 无 / 加密 / 配对+加密 / MITM 保护 ④ CCCD 描述符配置 使能 Notification / Indication 位 ⑤ 注册到 GATT 服务 att_read_cb / att_write_cb 绑定 关键注意事项 · Notify/Indicate 必须包含 CCCD 描述符 · 安全权限在属性之后 独立声明

2.2 读写属性原理与声明

2.2.1 属性类型及含义

每个 GATT 特征在声明时需指定一组属性标志(Properties),它们以位掩码形式组合,决定客户端对特征值的操作权限。

属性标志掩码值含义典型用途
Read0x02允许客户端读取特征值传感器数据、状态
Write0x08允许客户端写入特征值(需应答)控制命令、配置参数
Write Without Response0x04允许写入但无需应答高速数据流、日志
Notify0x10服务器主动通知(无应用层应答)实时测量值
Indicate0x20服务器主动指示(需应用层应答)可靠事件上报
设计要点:Notify 与 Indicate 必须配合 CCCD 描述符才能生效;客户端通过写 CCCD 使能后,服务器方可发送数据。Write Without Response 适用于频繁小数据写入,但需自行处理流控。

2.2.2 属性组合规则

实际开发中,一个特征常组合多个属性。以下为常用组合模式:

  • 只读(Read Only):传感器数值、设备信息。
  • 读写(Read + Write):可配置参数。
  • 通知使能(Read + Notify):允许客户端读取当前值并订阅更新。
  • 指示使能(Read + Indicate):需要可靠确认的事件通知。
  • 可写通知(Write + Notify):控制命令并回显状态。

2.3 安全权限配置

2.3.1 安全层级模型

BLE 安全定义了三级保护:

  1. 无安全(No Security):任何设备均可直接访问,无需加密或配对。
  2. 加密(Encryption):需配对并建立加密链路,未加密连接将被拒绝。
  3. 配对 + 加密 + MITM 保护:最高级别,要求配对过程具备中间人攻击防护能力。
安全级别要求适用场景
无无公开信息、调试
加密已配对并加密私有传感器数据
配对+加密+MITM带 MITM 保护的配对门锁、支付、健康隐私

2.3.2 权限声明 API 示例

以下为 Zephyr RTOS 中声明特征属性及安全权限的典型代码:

/* 特征属性定义:读 + 通知 */
#define MY_CHARACTERISTIC_PROP  (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY)

/* 安全权限定义:加密读(仅允许加密连接读取) */
#define MY_CHAR_READ_PERM       BT_GATT_PERM_READ_ENCRYPT
#define MY_CHAR_WRITE_PERM      BT_GATT_PERM_WRITE_ENCRYPT

/* 特征声明宏 */
BT_GATT_CHARACTERISTIC(
    BT_UUID_DECLARE_128(MY_CHAR_UUID),
    MY_CHARACTERISTIC_PROP,
    MY_CHAR_READ_PERM,
    MY_CHAR_WRITE_PERM,
    my_char_read_cb,
    my_char_write_cb,
    &my_char_value
);
权限粒度:权限可分别设置给读操作和写操作。例如允许未加密读,但加密写,以此平衡易用性与安全性。

2.4 CCCD 配置机制

2.4.1 CCCD 的作用

CCCD 是 GATT 标准描述符(UUID 0x2902),长度为 2 字节,位于特征声明之后。它控制服务器是否主动向客户端发送 Notify 或 Indicate 数据。

  • bit 0:Notify 使能(1 开启 / 0 关闭)
  • bit 1:Indicate 使能(1 开启 / 0 关闭)
  • 注意:Notify 和 Indicate 不可同时使能。

2.4.2 CCCD 配置流程

  1. 客户端发现特征后,通过 Read By Type Request 找到 CCCD 句柄。
  2. 客户端向 CCCD 句柄写入 0x0001(使能 Notify)或 0x0002(使能 Indicate)。
  3. 服务器收到写请求后,更新内部 CCCD 标志位,并返回 Write Response。
  4. 服务器在后续事件中检查标志位,决定是否发送数据。
/* 客户端使能 Notify 的 ATT 命令示例 */
ATT Write Request:
  Handle: 0x001A          /* CCCD 句柄 */
  Value:  0x0001          /* 使能 Notify */

ATT Write Response:
  Handle: 0x001A          /* 成功确认 */

2.4.3 服务器端 CCCD 状态同步

服务器必须在特征定义中预留 CCCD 存储空间,通常由 BLE 协议栈自动管理。但在某些平台上,需要手动处理 CCCD 的持久化与恢复,以确保设备重启后状态一致。

/* Zephyr 中为特征添加 CCCD 描述符 */
BT_GATT_DESCRIPTOR(
    BT_UUID_GATT_CCC,
    BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
    NULL,
    my_ccc_write_cb,
    &my_ccc_value
);
常见陷阱:CCCD 状态仅保存在 RAM 中,设备断开连接或重启后,客户端必须重新使能 Notify/Indicate。如需持久化,开发者应自行将 CCCD 值写入 NVS 或 Flash。

2.5 调试与验证方法

2.5.1 使用 nRF Connect 验证属性

  1. 连接设备后,进入 GATT Client 视图,查看特征列表。
  2. 点击特征行,检查属性标志是否与声明一致(Read/Write/Notify 图标高亮)。
  3. 尝试读取特征值,观察是否弹出安全对话框(如需要配对)。
  4. 使能 CCCD Notify,向服务器发送触发事件,确认是否收到通知包。

2.5.2 日志关键点

服务器端应输出以下信息以辅助调试:

/* 建议日志输出 */
[D] GATT: characteristic declared UUID=xxxx, props=0x12
[D] GATT: CCC write handle=0x001A value=0x0001
[D] ATT: notify sent to client handle=0x001B len=8
[E] SEC: access denied – not encrypted

2.6 常见错误与解决方案

问题现象根因解决方案
客户端读取特征值返回 0x0E(Not Supported)未声明 Read 属性在属性标志中添加 BT_GATT_CHRC_READ
写操作无响应(Write Timeout)未声明 Write 属性或权限拒绝检查属性标志与安全权限配置
使能 Notify 后收不到通知CCCD 未正确写入或服务器未检查标志监听 CCCD 写回调并更新内部状态
读操作弹出配对对话框但配对后仍失败权限要求 MITM 保护但配对未开启 MITM使用 IO Capabilities 并启用 MITM 配对
属性标志包含 Notify 但客户端看不到 CCCD缺少 CCCD 描述符声明在特征后添加 BT_GATT_DESCRIPTOR(BT_UUID_GATT_CCC, ...)

2.7 性能与稳定性优化

  • 减少属性组合的复杂性:一个特征只承担一种职责,避免同时启用 Notify 和 Indicate。
  • 安全权限分层:高频读取的数据使用加密即可,无需 MITM,以降低配对耗时。
  • CCCD 批量同步:若连接断开后恢复,建议客户端一次性写回所有 CCCD,避免逐个配置产生 ATT 流量风暴。
  • 缓存 CCCD 状态:在服务器端 NVS 中缓存 CCCD 值,重启后自动恢复,减少客户端重配置步骤。
  • 使用 bonded 设备信息:利用 BLE bonding 存储 CCCD 状态,协议栈可自动恢复通知使能。
本章小结:特征的读写属性、安全权限与 CCCD 配置共同构成了 GATT 服务端数据访问的完整控制面。属性决定操作类型,权限决定安全门槛,CCCD 决定主动推送开关。三者独立配置、协同工作,理解其原理是构建可靠 BLE 应用的基石。