蓝牙核心规范(V5.2)7.6-深入详解之GATT(1)

 蓝牙篇之蓝牙核心规范(V5.2)深入详解汇总


蓝牙核心规范(V5.2)7.6-深入详解之GATT(1)

 1.GATT定义

 通用属性协议(GATT)使用属性协议定义了一个服务框架。该框架定义了服务的程序和格式及其特征。所定义的过程包括发现、读、写、通知和指示特征,以及配置特征的广播。

 1.1 GATT协议的依赖性

蓝牙核心规范(V5.2)7.6-深入详解之GATT(1)
图1.1:协议依赖关系

 图1.1描述了概要协议的结构和依赖关系。如果协议通过隐式或显式引用来重用该协议的部分,则依赖于另一个协议。

 2.协议概述

 GATT协议设计的目的

供应用层和其他协议使用,以便客户端可以与服务器通信。

GATT协议的作用 

如何使用属性协议来发现、读取、写和获取这些属性的指示,以及配置属性的广播。

 2.1 协议栈

蓝牙核心规范(V5.2)7.6-深入详解之GATT(1)

 2.2 配置和角色

 客户端

这是一种向服务器启动命令和请求的设备,并且可以接收服务器发送的响应、指示和通知。

 服务器

该设备可以接收来自客户端的传入命令和请求,并向客户端发送响应、指示和通知。

 注意:这些角色没有固定到设备上。角色在设备启动已定义的过程时确定,并在过程结束时释放。

一个设备可以同时扮演这两个角色。

 图2.2描述了一个说明此配置文件的角色的配置示例。

蓝牙核心规范(V5.2)7.6-深入详解之GATT(1)
图2.2:配置的示例

 解析:

计算机是客户端,温度传感器是服务器。

计算器启动时配置传感器或者读取传感器的值。

在本示例中,传感器提供有关传感器设备作为温度服务的一部分所暴露的特性的信息,并可能允许写入某些特征。此外,传感器的值可以响应计算机的读取请求。

 2.3 用户需求和场景

 此配置文件涵盖以下场景:

  • 交换配置
  • 发现设备上的服务和特征
  • 读取特征值
  • 编写特征值
  • 特征值的通知
  • 特征值的指示

 2.4 协议基本原理

这个协议VB可以在任何物理链路上使用,使用属性协议L2CAP通道,称为ATT收听器。下面简要总结了客户端和服务器之间的底层需求通信。

  • 使用“通道建立”建立ATT承载器
  • 配置文件角色不与控制器主/从属角色绑定。
  • 在LE物理链路上,使用授权、身份验证和加密等安全功能是可选的。在BR/EDR上,必须进行物理链路加密。
  • 除特征值字段外,应首先发送最不显著的多八元字段(小中位数)。特征值和其中的任何字段都应该是小中值的。
  • 可以在客户端和服务器之间建立多个ATT承载器。服务器可以通过使用诸如对等设备的蓝牙设备地址之等的连接信息来确定ATT承载器是否来自同一客户端。

 2.5 属性协议

 GATT协议要求实现属性协议(ATT)和属性协议pdu。

2.5.1 概述

GATT协议使用属性协议以命令、设备之间的请求、响应、指示、通知和确认的形式传输数据。该数据包含在属性协议pdu中指定的属性协议pdu中。 

蓝牙核心规范(V5.2)7.6-深入详解之GATT(1)
图2.3:属性协议PDU

 操作码包含特定的命令、请求、响应、指示、通知或确认操作码以及用于身份验证的标志。

属性参数包含特定命令或请求的数据,或在响应、指示或通知中返回的数据。

认证签名是可选的。

 属性协议命令和请求作用于在服务器设备上的属性中存储的值。属性由四个部分组成:属性句柄、属性类型、属性值和属性权限。图2.4显示了一个属性的逻辑表示形式。对给定实现的实际表示是特定于该实现的。

蓝牙核心规范(V5.2)7.6-深入详解之GATT(1)
图2.4:逻辑属性表示

 属性句柄是对应于特定属性的索引。

属性类型是一个描述属性值的UUID。

属性值是由属性类型描述的数据,并由属性句柄进行索引。属性将通过增加属性句柄值来对其进行排序。属性句柄值可以从0x0001到0xFFFF之间的任何值开始。虽然属性句柄值按递增顺序排列,但以下属性句柄值可能有多个差异。也就是说,在连续的属性处理程序之间可能存在间隙。当规范要求两个属性句柄相邻或一个立即跟随另一个时,这些间隙仍然被允许,并应被忽略。

属性权限是不能从属性协议读取或写入的属性的一部分。服务器使用它来确定是否允许对给定属性的读访问或写访问。属性权限由GATT配置文件、更高层次的配置文件建立,或者如果未指定,则特定于实现。

 2.5.2 属性缓存

属性缓存是一种优化,它允许客户端发现属性信息,如服务器曾经使用的属性处理,并在重新连接中使用相同的属性信息,而无需重新发现。如果客户端没有缓存属性信息,则它必须在每次重新连接时重新发现属性信息。通过缓存,可以节省时间,并且在客户端和服务器之间不需要交换大量的数据包。客户端应缓存的属性信息是所有服务器属性的属性处理程序和GATT服务特性值。

服务器使用的属性处理程序不应随时间而变化。这意味着,一旦客户端发现了一个属性句柄,就不应更改该属性的属性句柄。

在某些情况下,可能会导致服务器更改用于服务的属性处理程序,这可能是由于工厂重置或正在执行的固件升级过程。只有在可以添加、修改或删除服务器上的服务时,服务器上才需要以下操作。如果在设备的可用生命周期内无法更改服务器上基于GATT的服务,则服务器上不存在服务更改特性,并且在该服务器的初始服务发现之后,客户端不需要执行服务发现。

为了支持服务器支持基于GATT的服务的更改时的缓存,当在服务器上添加、删除或修改服务时,服务器会向客户端发送指示。如果服务器上存在数据库哈希特征,客户端也可以通过读取数据库哈希特征来检测服务变化。如果属性处理与服务定义中分组的关联属性的绑定被更改,则认为基于GATT的服务将被修改。除服务更改特征值和客户支持特征特征值本身外,GATT服务定义特征值的任何更改也应被视为修改。

对于与服务器具有可信关系(即绑定)的客户端,属性缓存可跨连接有效。对于具有可信关系而不是在服务更改时进行连接的客户端,服务器应在客户端重新连接到服务器时发送指示。对于与服务器没有受信任关系且不支持读取数据库哈希特性的客户端,属性缓存仅在连接期间有效。没有支持读取数据库散列特性的受信任关系的客户端可能会验证连接设置上验证属性缓存。当服务的更改仅在当前连接期间发生时,没有受信任关系的客户端应收到一个指示。

注意:如果服务器支持服务缓存更改特性,则没有支持缓存的可信关系的客户端必须执行服务发现或通过读取每个连接上的数据库哈希特性来检测服务更改。 

 服务器应发送一个ATT_HANDLE_VALUE_INDPDU,其中包含在客户端属性缓存中无效的受影响的属性处理范围。启动属性句柄应是包含更改的服务定义的启动属性句柄,结束属性句柄应是包含更改的服务定义的最后一个属性句柄。指示中的值由两个连接的16位属性处理柄组成,以指示受影响的属性处理柄范围。

注意:服务器可能会将受影响的属性句柄范围设置为0x0001到0xFFFF,以向客户端指示重新发现服务器上的整个属性句柄集。

 如果服务器上存在数据库哈希特性,则每次发生服务更改时,服务器应使用新的数据库哈希值更新数据库哈希特性值。如果服务器上存在数据库哈希特性,则每次发生服务更改时,服务器应使用新的数据库哈希值更新数据库哈希特性值。

 如果数据库哈希特性值自上次读取以来发生了变化,客户端应认为其属性缓存无效,在执行服务发现或使用带外机制获得已更改的数据库定义之前,不得使用缓存的信息。

客户端在收到包含受影响的属性处理范围的ATT_HANDLE_VALUE_INDPDU后,应认为属性缓存在受影响的属性处理范围内无效。如果属性处理柄包含在受影响的属性处理柄范围内,则任何未处理的请求事务将被视为无效。客户端必须在使用受影响属性句柄范围内的任何服务之前执行服务发现。或者,客户端可以读取数据库哈希特性,并使用带外机制获取已更改后的数据库定义。如果客户端在服务发现期间接收到ATT_HANDLE_VALUE_INDPDU,并且客户端在服务发现之前已经读取了数据库哈希特性,则客户端可以再次读取数据库哈希特性,以确定是否可以对当前服务发现进行继续,或者是否需要新的服务发现。

一旦服务器收到ATT_HANDLE_VALUE_CFMPDU,服务器就可以考虑客户端知道更新的属性处理.

客户端应认为受影响的属性句柄范围在其属性缓存中无效,并执行发现过程以恢复属性缓存。服务器应存储所有绑定设备的服务变更信息。

稳健的缓存

         稳健缓存是一种功能,如果服务器不认为客户端意识到服务更改,服务器就会向客户端发送ATT_ERROR_RSPPDU。

        如果数据库哈希和服务更改特性都存在于服务器上,则服务器应支持健壮缓存特性。从服务器的角度来看,每个连接的客户端对数据库定义中的更改都是“知道更改”或“不知道更改”。连接到服务器后,没有受信任关系的客户端的初始状态是具有更改感知功能的。具有受信任关系的客户端的初始状态与以前的连接保持不变,除非数据库自上次连接以来已经更新,在这种情况下,初始状态是不知道更改的。

        每当服务器更新数据库定义时,所有连接的客户端都会变得不知道更改。当一个不知道更改的连接客户端读取数据库哈希特性时,然后服务器从客户端接收到另一个ATT请求时,它就会知道更改(参见图2.6)。此外,当发生以下任一种情况时,只使用一个ATT承载的没有更改的连接客户端就会知道更改:

蓝牙核心规范(V5.2)7.6-深入详解之GATT(1)
图2.5:由服务变更确认触发的向变更感知状态的转换

蓝牙核心规范(V5.2)7.6-深入详解之GATT(1)
图2.6:由ATT响应“数据库不同步”或“哈希读取”触发的更改感知状态的转换


 2.6 GATT 协议层 

 2.6.1 概述

GATT配置文件指定了交换配置文件数据的结构。此结构定义了在配置文件中使用的服务和特性等基本要素。所有的元素都由属性包含。属性协议中使用的属性是携带此配置文件数据的容器。

 层次结构的顶层是一个配置文件。配置文件由实现用例所需的一个或多个服务组成。服务由其他服务的特征或包含物组成。每个特性都包含一个值,并可能包含关于该值的可选信息。服务和特性以及特性的组件(即值和描述符)包含配置文件数据,并全部存储在服务器上的属性中。

蓝牙核心规范(V5.2)7.6-深入详解之GATT(1)
图2.7:GATT配置文件层次结构

 2.6.2 服务

服务是用来完成特定功能或特性的数据和相关行为的集合。在GATT中,一个服务是由其服务定义来定义的。服务定义可能包含所包含的服务、强制性特征和可选特征。

 服务有两种类型:主服务和二级服务。主服务是公开该设备的主要可用功能的服务。一个主服务可以由另一个服务包含。可以使用主服务发现过程来发现主服务。辅助服务是仅打算从主服务或其他辅助服务或其他更高级层规范中包含的服务。二级服务只与包含它的实体的上下文相关。

确定服务是主服务还是辅助服务可以由更高的层规范强制执行。

 服务可以用于一个或多个更高级的规范,以满足特定的用例。

2.6.3 Included services

所包含的服务是将服务器上现有的另一个服务定义引用到正在定义的服务中的方法。要包含另一个服务,则在服务定义的开始处使用一个包含定义。当服务定义使用包含定义来包含服务时,整个包含的服务定义将成为新服务定义的一部分。这包括所有所包含的服务和所包含的服务的特征。所包含的服务仍然作为一个独立的服务存在。另一项服务所包含的服务不得因包含行为或包含服务而改变。服务定义中包含定义的数量或嵌套包含的深度没有限制。

 2.6.4 characteristic(特征)

 特征是在服务中使用的值,以及关于如何访问该值的属性和配置信息,以及关于该值如何显示或表示的信息。在GATT中,一个特征是由其特征定义来定义的。特征定义包含特征声明、特性属性和值,并可能包含描述服务器关于属性的值或允许配置的描述符。

2.7 配置广播

配置广播只支持在LE物理链路中

配置广播不支持在BR,EDR物理链路中

3.服务的互操作性要求

3.1 服务定义

服务定义应包含服务声明,并可能包括包括定义和特征定义。服务定义在下一个服务声明之前或达到最大属性句柄之后结束。服务定义以基于属性句柄的顺序出现在服务器上。

所有包括服务定义中包含的定义和特征定义都被认为是服务的一部分。所有包括定义应立即在服务声明之后,并在任何特征定义之前。服务定义可能有零或更多个包含定义。所有特征定义应立即在最后一个包括定义之后,如果不包括定义,则应立即在服务声明之后。服务定义可以具有零或多个特征定义。包含或特征定义没有上限。

服务声明是一个属性,其属性类型设置为主服务或二级服务的UUID。属性值应为该服务的16位蓝牙UUID或128位UUID,称为服务UUID。客户端应支持同时使用16位和128位uuid。客户端可以忽略具有未知服务UUID的任何服务定义。未知的服务UUID是针对不受支持的服务的UUID。属性权限应是只读的,不需要身份验证或授权。

当存在多个服务时,使用16位蓝牙UUID的服务声明的服务定义分组在一起(按顺序列出),使用128位UUID的服务声明的服务定义分组在一起。

蓝牙核心规范(V5.2)7.6-深入详解之GATT(1)

 设备或更高级别的规范可能具有多个服务定义,并且可能具有具有同一服务UUID的多个服务定义。

服务器上的所有属性都应包含服务声明或存在于服务定义中。

 服务器中包含的服务定义可以以任何顺序出现;客户端不得承担服务器上的服务定义的顺序。

3.2  include definition

包含定义应只包含一个包含声明。

包含声明是一个属性,属性类型设置为包含的UUID。属性值应设置为包含的服务属性句柄、端组句柄和服务UUID。服务UUID仅在UUID为16位蓝牙UUID时才存在。属性权限应只读,不需要身份验证或授权。

蓝牙核心规范(V5.2)7.6-深入详解之GATT(1)

 服务器不应包含对包含原始服务的其他服务的包含定义的服务定义。这适用于包含的定义引用的每个服务。这被称为循环引用。

如果客户端检测到循环引用或检测到嵌套包含超过预期级别的声明,那么它应该终止ATT承载。

 3.3 CHARACTERISTIC DEFINITION(特征定义)

 特征定义应包含特征声明、特征值声明,并可能包含特征描述符声明。特征定义在下一个特征声明或服务声明的开始,或在最大属性句柄之后结束。特性定义以基于属性句柄的顺序在服务定义中出现在服务器上。

上面的每个声明都包含在一个单独的属性中。需要的两个声明是特征声明和特征值声明。特征值声明应在特征声明之后立即存在。任何可选的特征描述符声明都被放置在特征值声明之后。可选的特征描述符声明的顺序并不显著。

特征定义可以定义为将多个特征值连接成一个聚合的特征值。这可以用来通过读写单个聚合的特征值来优化多个特征值的读写。这种类型的特征定义与一个正常的特征定义相同。特征声明应使用唯一的特征UUID聚合的特征定义。聚合特征定义还可以包含特征聚合格式描述符,它描述聚合特征值的显示格式。

3.3.1 特征声明

特征声明是一个属性,属性类型设置为UUID,属性值设置为特征属性、特征值属性句柄和特征UUID。属性权限应具有可读性,不需要身份验证或授权。

如果服务器与任何特征声明具有信任关系时,服务器更改了任何特征声明属性值,则应向每个客户端发送服务更改指示,指示持有特征声明的服务的更改。

蓝牙核心规范(V5.2)7.6-深入详解之GATT(1)

 特性声明的属性值为只读值。

蓝牙核心规范(V5.2)7.6-深入详解之GATT(1)

 一个服务可以具有相同特征UUID的多个特征定义。

 在服务定义中,某些特征可能是强制性的,这些特征应定位在包括声明之后和服务定义中的任何可选特征之前。客户不得承担强制性特征的任何顺序或服务定义中可选特征的任何顺序。只要有可能,并且在前面所述的要求范围内,应该对使用16位蓝牙uuid的特征声明的特征定义进行分组一起(即按顺序列出)和具有使用128位uuid的特征声明的特征定义应该被组合在一起。

 固有特性 

 特征特性位字段决定了如何使用特征值,或如何访问特征描述符。如果设置了表3.5中定义的位,则允许所描述的操作。可以设置多个特征属性。

这些位应根据高层规范定义的该特性允许的程序进行设置,而不考虑安全要求。

蓝牙核心规范(V5.2)7.6-深入详解之GATT(1)

 特征值属性句柄

 特征值属性句柄字段是包含特征值的属性的属性句柄。

 特征UUID

 特征UUID字段是一个16位蓝牙UUID或128位UUID,用或描述特征值的类型。客户端应支持16位和128位特征uuid的使用。客户端可以忽略任何具有未知特征UUID的特征定义。未知特征UUID是不支持特征的UUID。

3.3.2 特征值声明

 特征值声明包含特征的值。它是在特征声明之后的第一个属性。所有的特征定义都应带有特征值声明。

特征值声明是一个属性,属性类型设置为16位蓝牙或128位UUID,用于特征声明中使用的特征值。属性值被设置为特征值。属性权限由服务指定,如果没有另行指定,则可能特定于实现。

蓝牙核心规范(V5.2)7.6-深入详解之GATT(1)

3.3.3 特征描述符声明 

特征描述符用于包含有关特征值的相关信息。GATT配置文件定义了一组标准的高层描述文件可以使用这些特征描述符。更高层次的配置文件可以定义特定于配置文件的其他特征描述符。每个特征描述符都由特征描述符UUID进行标识。客户端应支持同时使用16位和128位特征描述符uuid。客户端可以忽略具有未知特征描述符UUID的任何特征描述符声明。一个未知的特征描述符UUID是一个不受支持的特征描述符的UUID。

如果在特征定义中存在特征描述符,应遵循特征值声明。特征描述符声明可以以特征定义范围内的任何顺序出现。客户不得承担在特征值声明之后的特征描述符声明在特征定义中出现的顺序。

特征描述符声明权限由更高的层配置文件定义或特定于实现。客户端不得假设所有的特征描述符声明都是可读的。

特征扩展特性

 特征描述符包含在属性中,属性类型应设置为特性扩展特性的UUID,属性值应为特性扩展特性位字段。属性权限应具有可读性,而不需要身份验证和授权。

蓝牙核心规范(V5.2)7.6-深入详解之GATT(1)

 “特性扩展特性”位字段描述了关于如何使用特性值或如何访问特性描述符的附加属性。如果设置了表3.8中定义的位,则允许所描述的操作。可以设置多个附加属性。

属性

Bit数

描述

可靠的写

0

如果设置,则允许可靠地写入特征值

可写辅助词

1

如果设置,则允许写入特征描述符

 特征用户描述

 特征用户描述声明是一个可选的特征描述符,它定义了一个变量大小的UTF-8字符串,该字符串是特征值的用户文本描述。如果设置了特性属性的可写辅助位,则可以写入此特征描述符。特征描述符可以出现在特征值之后的特征定义内的任何位置。在特征定义中,只应存在一个特征用户描述声明。

 特征描述符包含在一个属性中,属性类型应设置为特征用户描述的UUID,属性值应设置为特征用户描述UTF-8字符串。属性权限由配置文件指定,如果没有另有指定,也可能是特定于实现的。

蓝牙核心规范(V5.2)7.6-深入详解之GATT(1)

 客户端特性配置

 客户端特性配置声明是一个可选的特征描述符,它定义了特定客户端如何配置特征。客户端特征配置描述符值应在绑定设备的跨连接中持久化。客户端特征配置描述符值应设置为与非绑定设备连接的默认值。特征描述符值为一个位字段。当设置一个位时,应启用该操作,否则将不使用。客户端特征配置描述符可能出现在特征值之后的特征定义内的任何位置。在特征定义中,只应存在一个客户端特征配置声明。

客户端可以编写此配置描述符来在服务器上控制此特性的配置。每个客户端都有自己的客户端特性配置的实例化。读取客户端特性配置只显示该客户端的配置,而写入只影响该客户端的配置。服务器编写配置描述符可能需要身份验证和授权。客户端特征配置声明应具有可读和可写。

特征描述符包含在一个属性中。属性类型应设置为客户端特征配置的UUID。属性值应设置为特征描述符值。属性权限由配置文件指定,如果没有另行指定,也可以特定于实现。

蓝牙核心规范(V5.2)7.6-深入详解之GATT(1) 定义了以下客户端特征配置位:

蓝牙核心规范(V5.2)7.6-深入详解之GATT(1)

 客户端特征配置描述符值的默认值应为0x0000。

在客户端和服务器之间应该有一个客户端特征配置描述符,而不管它们之间的ATT承载器的数量如何。

服务器特性配置

服务器特性配置声明是一个可选的特征描述符,它定义了如何为服务器配置特性。特征描述符值为一个位字段。当设置一个位时,应启用该操作,否则将不使用。服务器特征配置描述符可能出现在特征值之后的特征定义内的任何位置。在特征定义中只能存在一个服务器特征配置声明。服务器特性配置声明应具有可读和可写。

客户端可以编写此配置描述符来控制服务器上所有客户端的此特性的配置。对于所有客户端,都有一个服务器特性配置的单一实例化。服务器读取特性配置显示了所有客户端的配置,并且写入会影响所有客户端的配置。服务器编写配置描述符可能需要身份验证和授权。

特征描述符包含在一个属性中。属性类型应设置为服务器特性配置的UUID。属性值应设置为特征描述符值。属性权限由配置文件指定,如果没有另有指定,也可能是特定于实现的。

蓝牙核心规范(V5.2)7.6-深入详解之GATT(1)

 定义了以下服务器特征配置位:

蓝牙核心规范(V5.2)7.6-深入详解之GATT(1)

0- 如果有广告数据资源可用,服务器在广播程序时广播特征值。只有在特性的属性设置了广播位时,才能设置此值。

特征性的展示格式

 特征表示格式声明是一个可选的特征描述符,它可定义特征值的格式。特征描述符可以出现在特征值之后的特征定义内的任何位置。如果特征定义中存在多个特征表示格式声明,则特征聚合格式声明应作为特征定义的一部分存在。

特征格式值由格式、指数、单位、名称空间和描述五个部分组成。

特征描述符包含在一个属性中。属性类型应设置为UUID的特征表示格式。属性值应设置为特征描述符值。属性权限应为只读权限,不需要身份验证或授权。

蓝牙核心规范(V5.2)7.6-深入详解之GATT(1)

 特征表示格式描述符属性值字段的定义如下。

蓝牙核心规范(V5.2)7.6-深入详解之GATT(1)

 位序:小端格式

格式:格式字段决定如何格式化特征值中包含的单个值。如果一种格式不是整个八位数,则数据应存储在可以包含该值的最小八位数中。除最后一个八重奏最重要的位外,数据应占据每个八位的全部;最后一个八位中的所有其他位应保留将来使用。

蓝牙核心规范(V5.2)7.6-深入详解之GATT(1)

 当对IPv4地址进行编码时,应使用uint32格式类型。

当对IPv6地址进行编码时,应使用uint128格式类型。

在编码蓝牙BD_ADDR时,应使用uint48格式类型。

一个duint16是两个连接在一起的uint16值

Unit 单元:该单元是中定义的UUID,详见UUID详情

Name Space名字空间:“名称空间”字段用于标识在指定编号中定义的组织,该字段负责定义描述字段的枚举。

Description描述:描述是由“名称空间”字段标识的组织的分配编号[1]中定义的枚举值。

特征聚合格式

 特征聚合格式声明是一个可选的特征描述符,它定义了聚合特征值的格式。

特征描述符可以出现在特征值之后的特征定义内的任何位置。特征定义中只存在一个特征聚合格式声明。

特征聚合格式值由一个特征表示格式声明的属性句柄列表组成,其中每个属性句柄都指向一个特征表示格式声明。

属性权限应只读,不需要身份验证或授权。

蓝牙核心规范(V5.2)7.6-深入详解之GATT(1)

 属性柄列表是将多个16位属性柄值连接到单个属性值中。该列表应包含至少两个特性表示格式声明的属性句柄。特征值应通过属性处理程序所指向的每个特征表示格式声明进行分解。列表中的属性处理程序的顺序很重要。

如果在特征定义中存在多个特征表示格式声明,则还应有一个特征聚合格式声明。特征聚合格式声明应包括属性处理列表中属性定义中的每个特征表示格式声明。还可以使用来自其他特征定义的特征表示格式声明。

 3.4 GATT配置文件属性类型

 蓝牙核心规范(V5.2)7.6-深入详解之GATT(1)

蓝牙核心规范(V5.2)7.6-深入详解之GATT(1)

 

上一篇:Qt和MFC的比较


下一篇:所有笔记汇总目录