TI_BLE软件开发者指导7——属性(笔记)

来源:互联网 发布:淘宝网店名字女装 编辑:程序博客网 时间:2024/04/29 05:26

Texas Instruments CC2540/41
Bluetooth® Low Energy
Software Developer’s Guide
v1.3.2
Document Number:SWRU271F

《低功耗蓝牙开发权威指南》
Robin Heydon著,陈灿峰、刘嘉 译
机械工业出版社
2014.6

TI_BLE软件开发笔记7-属性

标签:读书笔记 BLE

  • TI_BLE软件开发笔记7-属性
    • 背景
      • 1 精简协议
      • 2 无所不在的数据
      • 3 数据与状态
      • 4 几种常见的状态
      • 5 状态机
      • 6 服务与规范
    • 属性
      • 1 属性描述
      • 2 属性句柄
      • 3 属性类型
      • 4 属性值
      • 5 数据库服务器和客户端
      • 6 属性许可
      • 7 接入属性
      • 8 原子操作和事务
    • 分组
    • 服务
      • 1 扩展服务
      • 2 其他服务的重用
      • 3 综合服务
      • 4 首要服务次要服务
      • 5 即插即用的客户端应用
      • 6 服务声明
      • 7 包含服务
    • 特性
      • 1 特性声明
      • 2 特性数值
      • 3 描述符
    • 属性协议
      • 1 协议消息
      • 2 交换MTU请求
      • 3 查找信息请求
      • 4 按类型值查找请求
      • 5 按类型读取请求
      • 6 读取请求
      • 7 大对象读取请求
      • 8 多重读取请求
      • 9 按组类型读取请求
      • 10 写入请求
      • 11 写入命令
      • 12 签名写入命令
      • 13 准备写入请求与执行写入请求
      • 14 句柄值通知
      • 15 句柄值指示
      • 16 错误响应
    • 通用属性规范
      • 1 发现规程
      • 2 发现服务
      • 3 特性发现
      • 4 客户端发起规程
      • 5 服务器发起规程
      • 6 属性协议数据单元ATT PDU到GATT规程的映射


包含属性层和通用属性规范层。属性协议被集成到核心规范时,SIG将其分割为一个抽象协议和一个通用配置文件。将通用属性规范从属性协议里抽象出来,理论上允许在属性协议上架构其他的通用规范。

1 背景

协议应当十分简单,因为任何的复杂性都会增加额外的成本和所需的存储空间;同时协议的数量要越少越好。BLE最后使用了三种协议:逻辑链路控制与适配协议(L2CAP)、安全管理协议(SM)和属性协议(AP)。
随后的目标便是尽可能减少协议的数量,让通用属性规范(GATT)之上的所有服务都是用属性协议(AP),包括用来提供名字和存在发现的通用访问规范(GAP)。这样在GATT上构建其他服务而成本被降至最低。

1.1 精简协议

BLE并不试图传输多样化的的数据类型,考虑到不会用来传输大批量的数据或是流媒体音乐,为其开发一种能够处理有限的几种数据类型的协议就可以。这种协议被称为属性协议,是整个蓝牙技术的基石和构造组件。

1.2 无所不在的数据

提供数据的设备成为服务器,使用其他设备的数据的设备成为客户端。

1.3 数据与状态

“状态”是指保存在服务器上的信息;数据指的是服务器传给客户端的信息,或是客户端当前持有的信息。因此服务器可以认为是一个持有许多状态信息的设备,客户端是读写该状态信息或者在本地缓存为数据的设备。由于客户端在获得服务器的数据时后者的状态可能已经发生了改变,因此客户端的数据不具有权威性。
设备具有状态,并将状态保留在服务器上。

1.4 几种常见的状态

BLE用到了三种不同种类的状态类型:外部状态、内部状态和抽象状态。
内部状态是设备当前的工作状态。
抽象状态是一种只和某一瞬间有关的状态信息,不反应设备当前的外部或内部状态。在属性协议里,这些抽象状态称作控制点,一般不可读取但可被写入或被通知。

1.5 状态机

有限状态机可以明确的使用属性协议和可公开的状态表示。状态机反映了设备的内部状态,并且有一个或多个外部的输入接口。这些外部输入为瞬时命令,根据其他状态信息或行为来改变状态机的状态,这是一个抽象状态,或称为控制点。

1.6 服务与规范

经典蓝牙的问题:现有的规范对网络中的设备自身的行为定义不够明确;现有的规范几乎无法用于当初没有预想过的新场景。
BLE采用了纯粹的“客户端——服务器”的结构,针对不同的用例对服务器和客户端的行为单独进行描述。服务器的行为在服务器规格书中定义,而用户的行为在另一规范说明中定义。通过一个属性数据库,服务器规格书定义了需要公开的状态以及通过属性可以实现的行为。
客户端规范从本质上是一组有关发现、连接、配置和使用各种服务的规则。同时,服务也包括了如何执行客户端要求的一系列操作的标准步骤。

2 属性

属性是一条带有标签的、可以被寻址的数据。

2.1 属性描述

属性的结构:
属性的结构

2.2 属性句柄

一个设备可以具有许多属性。表面看通过属性类型足以判断某种属性,但如果一个设备包含了室内室外两种温度值。通过属性句柄解决这个问题。
有效的句柄范围:0x0001~0xFFFF。0x0000无效句柄,不能用于寻址属性。

2.3 属性类型

所公开的数据的种类称作属性类型。为了区分众多的数据类型,采用128位的通用唯一识别码(UUID)来标识属性类型。
为了提高传输效率,SIG定义了一个称为“蓝牙UUID基数”的128位通用唯一识别码,结合一个较短的16位数使用。二者仍遵循UUID的分配规则,只不过在设备间传输常用的UUID时,只发送较短的16位版本,接收方收到后补上蓝牙UUID基数即可。
蓝牙UUID基数:

00000000-0000-1000-8000-00805F9B34FB

假设要发送的16位识别码位0x2A01,完整的128位UUID就是:

00002A01-0000-1000-8000-00805F9B34FB

16位UUID,通常不直接使用其数值,而是冠以一个名称并加上书名号。例如用《include》标识0x2802的UUID。UUID并没有定义自身的用法,为了增加人工调试时的可读性,BLE使用的那部分UUID被分为以下几组:

  • 0x1800~0x26FF 被用作服务类通用唯一标识码
  • 0x2700~0x27FF 用做标识计量单位
  • 0x2800~0x28FF 用作区分属性类型
  • 0x2900~0x29FF 用作特性描述
  • 0x2A00~0x7FFF 用作区分特性类型

2.4 属性值

属性值用于表示设备公开的状态信息,某些类型的属性值长度固定的。属性值对于属性协议来说并不重要,但对于通用属性规范和其之上的服务与规范来说有着相当重要的意义。
1.服务通用唯一识别码
每一种服务都能用一个UUID来辨认,既可以是16位,也可以是128位。
2.单位
为所有可能的数值定义相关单位的UUID。
3.属性类型
用于识别属性类型的UUID中包含了一些最基本的属性类型,一般用于通用属性规范,而非具体的服务。这些属性类型包括:

  • 首要服务
  • 次要服务
  • 包含
  • 特性

4.特性描述符
一些服务公开的数据包含了额外的信息。这种额外的信息便被冠以特性描述符。例如一个描述相关数值的格式的信息(格式与表示方式)便是所谓的描述符。
5.特性类型
服务为公开的每一类数值都分配了一个“特定类型”UUID。客户端从而能够发现服务器提供的所有不同类型的数据。每一个特性类型都有既定的格式和表达方式。

2.5 数据库、服务器和客户端

一组属性的集合称为数据库。属性数据库总是位于属性服务器中。通过属性协议,属性客户端和属性服务器进行通信。对于BLE设备而言,由于强制要求属性数据库支持GAP服务,每一部低功耗蓝牙设备都包含有一个属性服务器和一个属性数据库。

属性句柄 属性类型 属性值 0x0001 首要服务 GAP服务 0x0002 特性 设备名 0x0003 设备名 “接近标签” 0x0004 特性 外观 0x0005 外观 标签 0x0006 首要服务 GATT服务 0x0007 首要服务 发射功率服务 0x0008 特性 发射功率 0x0009 发射功率 -4dBm 0x000A 首要服务 立即报警服务 0x000B 特性 报警级别 0x000C 报警级别 0x000D 首要服务 连接丢失服务 0x000E 特性 报警级别 0x000F 报警级别 高 0x0010 首要服务 电量服务 0x0011 特性 电池余量 0x0012 电池余量 75% 0x0013 特性表达格式 8位无符号整形,0,百分比 0x0014 特性 电池余量状态 0x0015 电池余量状态 75%放电中 0x0016 客户端特性配置 0x0001

2.6 属性许可

一些属性服务器上的属性含有可读或可写信息。为了提供这类访问限制,数据库中每一个属性都含有一个许可。许可自身可以分为三种类型:使用许可、认证许可和授权许可。
属性许可只适用于属性值,并不适用于属性句柄和属性类型。每一台设备都有发现其他设备公开的所有属性的许可,包括属性句柄和类型。
定义的使用许可如下:

  • 可读
  • 可写
  • 可读并可写

定义的认证许可如下:

  • 需要认证
  • 不需要认证

定义的授权许可如下:

  • 无授权
  • 授权

授权和认证略有区别。客户端能够完全掌控发起认证的时机和方式。授权是服务器的一种行为,授权与否完全由服务器来决定,客户端对于授权不足无能为力。

2.7 接入属性

客户端可以通过使用下列任意一种消息类型来访问属性数据库的各个属性:

  • 寻找请求
  • 读取请求
  • 写入请求
  • 写入命令
  • 通知
  • 指示

客户端先使用“寻找请求”来寻找属性数据库中的属性,岁后才能使用效率更高的基于句柄的请求。
发送读取请求用以读取某个属性值。要确定读取的具体属性,应指定一个或多个属性句柄或者句柄范围,以及属性的类型。
发送写入请求用以携入某个属性值。该请求常常会使用一个属性句柄和要写入的数值。也可以事先准备多个需要写入的数值,然后在一个原子操作中一并写入。
这里的任意一种请求都会使属性服务器发送一条响应。如果还需请求更多的数据,客户端还需发送另一条请求。为了降低服务器设备的复杂度,客户端一次只能发起一条请求,只有收到了上一条请求响应以后,才能发起下一条请求。
还可以通过写入命令来写入属性值。该命令不要求服务器回复。写入命令可以在任意时刻发送。这说明可以把命令写入公开的状态机的控制点。
通知和指示都是由服务器自发的向客户端发送属性值。通知可以在任意时刻发送,包含了要通知的属性句柄和当前的属性值。指示也包含了属性句柄和属性值。不同的是指示需要客户端回传一条属性确认信息。客户端通过回复确认信息,即表明了指示已经送达,还告诉服务器设备可以发起下一条指示;相比之下,通知可以在任意时刻发送。

2.8 原子操作和事务

在客户端和服务器之间发送的每一条属性协议的信息都隶属于某个事务的一部分。事务可以是一条请求及其回应,也可以是一条指示及其确认信息。事务的重要性在于它限定了连续的事物之间需要保存的信息的数量。这意味着当设备接收到一条请求后,它无须为了处理下一条请求而保存当前请求的任何信息。
在一个事物结束之前不能发起新的事务。事务仅针对单个设备。发起事务的设备虽然不能初始化另一个事务,但可以处理其他配对设备的请求。
1. 命令和通知
借助于命令和通知,设备的已将信息发送给另一台设备,并在发起下一条命令或通知之前无需等待响应的响应。命令和通知完全可能被用来向对端设备发起泛洪攻击。
2. 准备写入请求与执行写入请求
使用准备写入请求和执行写入请求消息,设备可以准备一系列的写入操作并在一个事件中执行。从事件角度看,每一条准备写入请求及其响应属于一个单独事务。另外,在准备和执行操作的中途,允许插入其他请求。
这种命令的两种用法:长写入和校验写入。这两种准备写入请求不仅包含了要写入的属性的句柄和值,还包含了需要写入的数值在目标属性中的偏移量。这意味着你可以在一次操作中用一系列的准备写入请求来写入某个很长的属性值的不同部分。另外准备写入请求的响应也包含了原请求中的属性句柄、偏移量和待写入部分的数值。表面看浪费了带宽,但由于响应中包含的信息必须和原请求相同,这能够避免错误的出现。

3 分组

属性规范定义了多组属性。
面向对象编程中:接口是对外部行为的描述,类是对该接口的实现,对象是该类的实例
在BLE中,服务和特性都用到了分组。使用服务声明来对服务进行分组,用特性声明对特性进行分组。服务是一种或多种特性的组合;特性则由一种或多种属性组成。

4 服务

BLE中,通用属性规范定义了两种基本的分组模式:服务分组模式和特性分组模式。服务等同于一个具有不可变接口的对象,一般包含一种或多种特性,并能引用其他服务。特性是数据的单位或公开行为的单位,这些特性是自解释的,一般的客户端能够读取、显示这些特性。
一种服务只是一组特性和通过它们所公开的行为的集合。这些特性和它们对应的行为组成了服务的不可变的接口。

4.1 扩展服务

服务A已经使用了多年,现需要扩展其功能。由于服务A是不可变的,无法简单的在原服务中添加新的行为。因此必须保证不触及原有服务的同时作必要的拓展。为此定义了新的服务AB,包含所需的扩展行为。不过为了保证成千上万的仅支持A的现有设备具备后向兼容性,也必须在每一部提供服务AB的设备上包含服务A的实例。

4.2 其他服务的重用

另一个重用其他服务的方法是引用:服务A想使用服务B的行为和状态信息,那么服务A只需用用服务B即可。类似于一个指向另一个类的实例的通用指针。

4.3 综合服务

有时,需要将与某个服务相关的两个独立的服务实例结合起来,并使其具备额外的行为。为了用服务满足这种需求,必须定义第三种服务,该服务分别引用了原来两个服务。

4.4 首要服务次要服务

公开设备功能的服务通常属于首要服务。次要服务是一组无需用户理解的行为和特性的封装。
通过建立“服务树”,首要服务在树的顶端,每一个分支指向首要服务或次要服务。

4.5 即插即用的客户端应用

可以使用某设备上的服务树的集合,并搜寻能使用这些服务树的应用。通用客户端会先进行一次完整的服务枚举:先是首要服务,随后是它们与所引用的服务之间的关系。

4.6 服务声明

使用服务声明对服务进行归类。服务声明的值是服务的UUID。设备可以安全的忽略掉任何不理解的服务。为了实现这一功能,属性协议允许利用属性句柄的范围来发现服务,但只进一步处理已知的服务。

4.7 包含服务

次要服务必须逐个发现。为此,每一个服务可以有零或多个包含属性。包含的声明总是紧随着服务声明,并列于该服务的其他属性之前。包含的声明也包括了引用服务的句柄范围,以及引用服务的服务UUID。

5 特性

特性只是一个数值。特性还需指明数值的数据类型,数值是否可读或可写,如何配置数值的指示、通知、广播,数值的含义等。
特性由特性声明、特性数值和零个或多个描述符组成,声明包含了特性的所有其他属性,数值属性包含了该特性的实际值,描述符包含了该特性的附加信息或配置。

5.1 特性声明

要建立一个特性,首先要用到特性属性,包含三个字段:特性性质、数值属性句柄和特性类型。
特性性质是一个八位字段,确定了特性数值属性对一系列操作的支持情况,包括:读取、写入、通知、指示、广播、命令、签名认证。还包含一个扩展性质位。
数值句柄字段是包含特性数值的属性句柄。用户只需返回特性声明给服务器,后者使用该声明便可以立刻获取属性的数值。
特性UUID用来识别特性数值的类型,该UUID类型必须和包含该特性数值的属性一致。

5.2 特性数值

特性数值是一个属性,类型必须符合特性声明的特性UUID字段。

5.3 描述符

特性可以包含如下的描述符:

  • 特性扩展性质
  • 特性用户描述
  • 客户端特性配置
  • 服务器特性配置
  • 特性表示格式
  • 特性聚合格式

6 属性协议

属性协议是非常简单的协议,客户端通过它可以发现并获取属性服务器上的属性。它由六种基本操作构成:

  • 请求
  • 响应
  • 命令
  • 指示
  • 确认
  • 通知

客户端通过向服务器发送请求,以要求服务器执行相关操作并进行响应。客户端一次只能发送一条请求。请求有两种可能的响应:与请求直接相关的响应;或是说明请求为何失败的错误响应。过程如下所示:

    participant 客户端    participant 服务器    客户端->服务器: 属性协议请求    服务器->客户端: 属性协议响应

客户端也向服务器发送命令,但不一定会收到响应。如果响应延迟发送,也可以使用命令,此时命令将以指示或通知的形式发送。过程如下所示:

    participant 客户端    participant 服务器    客户端->服务器: 属性协议命令

服务器通过向客户端发送指示,让客户端获悉某属性的数值。指示和请求有些类似:客户端也需要返回一个确认响应。服务器只有在接收到上一条指示的确认以后才能发起下一条指示。过程如下所示:

    participant 客户端    participant 服务器    服务器->客户端: 属性协议指示    客户端->服务器: 属性协议确认

服务器以发送通知的形式来告知客户端某属性的数值。通知不要求响应,和命令类似。过程如下所示:

    participant 客户端    participant 服务器    服务器->客户端: 属性协议通知

由于命令和通知都不需要响应或确认,因此发送次数没有限制。如果发送了过多的命令和通知,服务器或客户端无法一一处理时,接收端可以将其忽视。

6.1 协议消息

下表列出了属性协议中所有属性协议数据单元(PDU):

消息 请求参数 响应参数 错误 错误操作码,错误句柄,错误码 交换MTU 客户端接收MTU 服务器接收MTU 查找信息 起始句柄,结束句柄 (句柄,类型) 按类型值查找 起始句柄,结束句柄,类型,数值 (查找到的句柄,结束组句柄) 按类型读取 起始句柄,结束句柄,类型 长度,(句柄,数值) 读取 句柄 数值 大数据读取 句柄,偏移量 部分值 多重读取 (句柄) (数值) 按组类型读取 起始句柄,结束句柄,组类型 (句柄,结束组句柄,数值) 写入 句柄,数值 准备写入 句柄,数值 句柄,数值 执行写入 标识 PDU 命令参数 写命令 句柄,数值 签名写命令 句柄,数值,认证签名 PDU 通知参数 句柄值通知 句柄,数值 PDU 指示参数 确认参数 句柄值指示 句柄,数值 服务器接收MTU

6.2 交换MTU请求

BLE中,属性协议默认的MTU(Maximum Transmission Unit,最大传输单元)长度为23字节。如果设备想发送更大的数据包,就要写上一个更长的MTU。交换MTU请求与交换MTU响应中发送的数值是固定的。

6.3 查找信息请求

查找信息请求和回复用来查找一系列属性的句柄和类型信息。这是唯一一个能让客户端发现任意属性类型的消息。
查找信息的请求包含起始句柄和结束句柄,定义了该请求用到的属性句柄范围。通常响应之恩能够包含该范围内的部分句柄;为了查找所有的属性,必须使用一连串的查找信息请求,相邻请求中的起始、结束句柄前后相接。
查找信息响应包含句柄-类型对。具有两种可能的格式,对应BLE使用的两种长度UUID:对于16位UUID,MTU默认为23字节,因此一个查找信息的响应最多包含5个句柄-类型对;对于128位UUID,只能包含一个句柄-类型对。更改MTU,所包含的句柄-类型对随之增加。

    participant 客户端    participant 服务器    客户端->服务器:查找信息请求(0x0100,0x01FF)    服务器->客户端:查找信息响应(0x1000,首要服务;0x0101,特性;\n0x0102,电池余量;0x0103,客户端特性配置)    客户端->服务器:查找信息请求(0x0104,0x01FF)    服务器->客户端:查找信息响应(0x0104,特性表达格式)    客户端->服务器:查找信息请求(0x0105,0x01FF)    服务器->客户端:错误响应

6.4 按类型值查找请求

使用按类型之查找请求和回复可以根据给定的类型和数值查找相应的属性。该请求包含两个句柄:起始句柄与结束句柄,定义了请求用到的属性句柄范围。对任意属性,当它的句柄处于起始与结束句柄范围内,并且具备了请求中所指定的类型和数值,那么该属性将在响应中被返回。
按类型值查找请求主要用途是查找特定的首要服务。

6.5 按类型读取请求

按类型读取请求能在句柄范围中读取某属性值。当客户端仅知道属性的类型而非句柄时可以使用该请求。请求包含了起始、结束句柄和需要读取的属性的类型。响应将给出符合的句柄和数值。
按类型读取请求用于搜索已包含的服务,并通过特性类型来发现服务中所有的特性。可被用来读取已知类型的特性值。

6.6 读取请求

请求包含一个句柄,响应将返回该句柄对应的属性值。只有在客户端已知属性句柄的情况下,才能使用该请求读取属性值。

6.7 大对象读取请求

有时属性值太长,无法装入一个读取响应,需使用大对象读取请求来获取剩余字节。大对象读取请求不光包含属性句柄,还包含属性值在整个数据中的偏移量。其相应将从属性偏移量开始,包含尽可能多的属性值。
在获取了属性值前22字节后,假如客户端还想获取后续的属性值,则应使用大对象读取请求。下一条响应将返回第23个字节到第44个字节,如此继续,直到客户端读取到完整的属性值。
该请求用于读取长特性值与长特性描述字。

6.8 多重读取请求

多重读取请求用来在一个操作中读取多个属性值。该请求包含一个或多个属性句柄,响应则按照请求的顺序返回相应的属性值。如果客户端请求读取的属性值长度超过了响应数据包所能承载的最大长度,那么无法放入响应数据包的数值将被丢弃。

6.9 按组类型读取请求

按组类型读取请求包含一个句柄的范围,读取时将其视为一个属性的类型来处理,只不过属性的类型必须为分组属性。其响应包含所读取的属性句柄、属性分组中最后一个属性以及属性的数值。

6.10 写入请求

请求包含了一个句柄和打算写入该属性的数值。响应中将确认该数值已经被写入。

6.11 写入命令

与写入请求类似,区别在于写入命令没有响应。

6.12 签名写入命令

和写入命令类似,但包含认证签名。发送端可以在向服务器发送写入命令时认证自己,而无需加密通信连接。

6.13 准备写入请求与执行写入请求

准备写入请求与执行写入请求实现了两个功能:第一,它们提供了长属性值的写入功能;第二,允许在一个单独执行的原子操作中写入多个值。属性服务器包含一个准备写入队列,其中保存有准备写入请求。队列的大小可以独立配置,但通常它足够存储所有需要准备写入的服务。只有在收到执行写入请求时,准备写入的值才会写入属性。
和大对象读取请求类似,准备写入请求包含句柄、偏移量和部分属性值。这说明客户端既可以在队列中准备多个属性的值,也可以准备一个长属性值的各个部分。在真正执行准备队列之前,客户端可以确定所有部分都能写入服务器。
准备写入响应也包含请求中所有的句柄、偏移量和部分属性值。之所以几乎偏执的重复这些数据是为了保证数据传递的可靠性。客户端可以对比响应和请求的字段值,保证准备的数据被正确的接收。
一旦接收完所有的准备写入请求,服务器将拥有一个随时可以执行的准备写入队列。客户端发送标志位为“立即写入”的执行写入请求,随后服务器将在一次原子操作中写入所有值。属性将按照其准备的顺序写入。如果客户端多次准备了同一属性值,那么服务器将按照顺序向该属性写入这些值。可以使用准备队列来配置硬件状态。

6.14 句柄值通知

当服务器想要向客户端发送快速的属性状态更新时,可以发送一条句柄值通知。句柄值通知包含属性句柄和数值。通知可以在任何时刻发送,因此适用于必须立即向客户端发送通知的情况。

6.15 句柄值指示

句柄值指示类似于句柄值通知,有着相同的属性句柄字段和数值,不同的是需要客户端回复。服务器一次只能发送一条指示,并且之后收到确认响应后才发起下一条指示。
句柄值确认不包含任何信息,主要用于流控。

6.16 错误响应

当设备无法完成请求所要求的操作时,发送错误响应。错误响应包含导致错误的、和请求相关的所有信息。一旦客户端收到错误响应,它会认为该响应与其发送的最后一条请求相对应。一条错误将终止当前请求。如果客户端修复了该错误,客户端必须重新发起该请求。

7 通用属性规范

属性协议定义了客户端与服务器如何相互发送符合标准的消息,而GATT规程定义了如何发现与使用服务、特性与描述符的标准方法。GATT规程可以分为三种基本类型:

  • 发现规程
  • 客户端发起规程
  • 服务器发起规程

交换MTU规程不属于上述分组,它使用属性协议中的MTU交换请求来确定随后的消息所使用的MTU长度。

7.1 发现规程

有四种基本对象需要发现。首先,客户端需要发现首要服务,一旦发现了首要服务,服务器上所有其他的隶属于该首要服务的信息都可以被发现。随后,客户端为每个首要服务设置句柄范围,已发现被引用的次要服务,以及该服务实例所公开的特性及其描述符。只有进行了上述操作,客户端才能使用这些服务,服务器发起规程才能使用该服务进行读写特征值或描述符的操作。

7.2 发现服务

有三种发现服务的途径:

1、发现所有首要服务
当客户端连接设备后,为了发现设备上公开的所有首要服务来判断设备的功能,客户端可以使用按组类型读取请求,请求的句柄范围0x0001~0xFFFF并将属性类型设为首要服务。随后服务器把发现的首要服务回复给客户端。当中既包含服务声明的句柄,也包含该服务中属性的最后一个句柄。响应也包含该服务的声明内容,这样客户端便能理解每一种服务。
如果最后一个服务的最后一个句柄不是0xFFFF或者收到了错误响应,客户端将发送另一条按组类型读取请求。该请求的起始句柄紧随着上一条响应中最后一个服务的最后句柄,这样客户端便能够枚举设备上的所有服务。

2、按服务UUID发现首要服务
客户端向服务器发送按类型值查找请求,将句柄范围设为0x0001~0xFFFF,把类型设为首要服务,并把数值设为想获取的已知服务类型。随后服务器将响应所找到的每一个指定服务的句柄范围。

3、查找包含服务
一旦发现了首要服务,便能发现次要服务与其他的引用服务。这个过程要使用按类型读取请求来查找一个包含声明。此时,应将起始句柄与结束句柄设置为之前发现的服务的句柄范围。

7.3 特性发现

在服务被发现以后,便可以发现每一个服务的特性。要获取完整的特性,需要发现特性和特性描述符。

1、发现服务的所有特性
为了发现特性,使用按类型读取请求,将句柄范围设为该服务的句柄范围,并把类型设为特性,这样便能发现并读取所有的特性声明。
响应这能够将包含服务中每一个特性的声明和句柄。特性声明包含它的特征、承载特性值的属性句柄以及该特性的类型。这意味着一旦发现了特性声明便能获悉该特性表述的内容、该特性的功用以及后续读写规程所用到的句柄。

2、发现所有特性描述符
发现了特性声明之后,便可以发现每一个特性的描述符。使用查找信息请求,将句柄范围设为归类于该特性的每一个特性声明的句柄范围。
查找信息响应包含该特性的所有描述符的句柄和类型。客户端可以安全的忽略掉它们无法理解的特性描述符。

7.4 客户端发起规程

对于特性,客户端可以执行四种相关操作:

  • 读取特征值
  • 写入特征值
  • 读取特性描述符
  • 写入特性描述符

1、读取(长)特性值
在发现了服务的特性之后,便可以读取特性值。特性声明中的某个句柄指向了保存该特性值的属性。该属性的类型也和特性声明中的特性UUID一致。因此,一旦使用了之前描述的特性发现规程发现了特性描述符,特性值便能够通过读取请求或大对象读取请求读取。
2、使用特性UUID读取
使用按类型读取请求,并将类型设为所要求的特性UUID。
3、读取多重特性值
如果已知每一个特性值的句柄,那么可以使用多重读取请求,同时读取多个特性值。每一个值之间没有特定的界限,因此必须知道每一个值的长度。
4、写入(长)特性值
写入请求被用来写入短特性值,默认的特性长度小于或等于20个字节。在使用该请求之前,必须先发现该特性值的属性句柄。
如果写入的属性值长度大于20个字节,需要准备写入请求来保存要写入的长值,以及执行写入请求用来真正执行写入操作。
5、特性值可靠写入
该规程使用了与写入长特征值规程中相同的准备写入请求与执行写入请求,稍有不同的是多了一个检验步骤,通过逐一比较准备写入响应与请求中的每一个值,保证了响应中的句柄和值与请求的完全一致。和6.13章节类似。
6、(签名)无需响应写入
有时,需要快速的将值写入,并且不需要协议层面的回复。此规程使用写入命令将需要写入的值发送给目标特性值属性。该命令没有响应。
对一些设备而言,向其写入数据时需要对该信息的发起者进行认证。为此用到了签名写入命令,同样不需要响应。认证提供了额外的安全性。
7、读/写(长)特性描述符
特性描述符不同于特性值,但获取规程与读写特性值的规程类似。
读取请求与大对象读取请求用来读取描述符,写入描述符则用到了写入请求和准备写入请求以及执行写入请求。

7.5 服务器发起规程

有两种GATT规程是由服务器发起的:

1、通知
没有流控机制。如果客户端没有足够的缓存空间来保存,允许丢弃。通知使用句柄值通知消息。
2、指示
包含流控机制,在服务器收到确认之前不能发送新的指示。指示使用句柄值只是消息和句柄值确认消息。

7.6 属性协议数据单元(ATT PDU)到GATT规程的映射

属性协议数据单元 GATT规程 交换MTU请求 交换MTU规程 查找信息请求 发现所有特性描述符规程 按类型值查找请求 按服务UUID发现首要服务规程 按类型读取请求 查找包含服务、发现所有服务特性、按UUID发现特性、按特性UUID读取规程 读取请求 读取特性值,读取特性描述符规程 大对象读取请求 读取长特性值,读取长特性描述符规程 多重读取请求 读取多重特性值规程 按组类型读取请求 发现所有首要服务规程 写入请求 写入特征值,写入特性描述符规程 写入命令 无需响应写入规程 签名写入命令 无需响应的签名写入规程 准备写入请求,执行写入请求 写入长特性值、特性值可靠写入、写入长特性描述符规程 句柄值通知 通知规程 句柄值指示 指示规程
0 0