《低功耗蓝牙开发权威指南》——3.2节主机-阿里云开发者社区

开发者社区> 华章出版社> 正文
登录阅读全文

《低功耗蓝牙开发权威指南》——3.2节主机

简介:

本节书摘来自华章社区《低功耗蓝牙开发权威指南》一书中的第3章,第3.2节主机,作者 (英)Robin Heydon,更多章节内容可以访问云栖社区“华章社区”公众号查看

3.2 主机
主机是蓝牙世界的无名英雄。主机包含复用层、协议和用来实现许多有用而且有趣的过程。主机构建于主机控制器接口的上层部分,其上为逻辑链路控制和适配协议(L2CAP),一个复用层。在它上面是系统的两个基本构建块:安全管理器(用于处理所有认证和安全连接等事务)以及属性协议(用于公开设备上的状态数据)。属性协议之上为通用属性规范,定义属性协议是如何实现可重用的服务的,而这些服务公开了设备的标准特性。最后,通用访问规范定义了设备如何以一种可交互方式找到对方并与之连接。
主机并未对其上层接口做明确规定。每个操作系统或环境都会有不同的方式公开主机的应用程序接口,不管是通过一个功能接口还是一个面向对象的接口。

3.2.1 逻辑链路控制和适配协议
逻辑链路控制和适配协议(L2CAP)是低功耗蓝牙的复用层。该层定义了两个基本概念:L2CAP信道和L2CAP信令。L2CAP信道是一个双向数据通道,通向对端设备上的某一特定的协议或规范。每个通道都是独立的,可以有自己的流量控制和与其关联的配置信息。经典蓝牙使用了L2CAP的大部分功能,包括动态信道标识符、协议服务多路复用器、增强的重传、流模式等。相比而言,低功耗蓝牙只用到了最少的L2CAP功能。
低功耗蓝牙中只使用固定信道:一个用于信令信道,一个用于安全管理器,还有一个用于属性协议。低功耗蓝牙只有一种帧格式,即B帧,包含两个字节的长度字段和两个字节的信道识别符字段,如图3-3所示。B帧的格式和传统L2CAP在每个通道使用的基本帧格式一致,在协商使用一些更复杂的帧格式之前,传统L2CAP会一直使用该帧格式。关于复杂帧格式的一个例子是经典蓝牙的包括了额外帧序列和校验值的帧。这些帧没有必要用在低功耗蓝牙中,因为链路层已有足够的校验强度,不必使用额外的校验值,而且简单的属性协议不会用多个信道乱序发送报文。通过保持协议的简单性和执行恰到好处的校验,只用一种帧格式也就足够了。


75aecc63bdb2e09b74eaf2e822572fbf9b5e6903

3.2.2 安全管理器协议
安全管理器定义了一个简单的配对和密钥分发协议。配对是一个获取对方设备信任的过程,通常采取认证的方式实现。配对之后,接着是链路加密和密钥分发过程。在密钥分发过程中从设备把秘密共享给主设备,当这两台设备在未来的某个时候重连时,他们可以使用先前分发的共享秘密进行加密,从而迅速认证彼此的身份。安全管理器还提供了一个安全工具箱,负责生成数据的哈希值、确认值以及配对过程中使用的短期密钥。

3.2.3 属性协议
属性协议定义了访问对端设备上的数据的一组规则。数据存储在属性服务器的“属性”里,供属性客户端执行读写操作。客户端将请求发送至服务器,后者回复响应消息。客户端可以使用这些请求在服务器上找到所有的属性并且读写这些属性。属性协议定义了六种类型的信息:1)从客户端发送至服务器的请求;2)从服务器发送至客户端的回复请求的响应;3)从客户端发送至服务器的无需响应的命令;4)从服务器发送至客户端的无需确认的通知;5)从服务器发送至客户端的指示;6)从客户端发送至服务器的回复指示的确认。所以,客户端和服务器二者都可以发起通信,发送需要对方回复的消息或者无需回复的消息。
属性是被编址并打上标签的一小块数据。每个属性均包含一个用来标识该属性的唯一的句柄、一个用于标识存储数据的类型以及一个值。例如,一个类型是“温度”、值为20.5℃的属性可能放在句柄为0x01CE的属性里。属性协议没有定义任何属性类型,但规定某些属性可以分组,并且可以通过属性协议发现分组的语义。
属性协议还定义了一些属性的权限:如果客户端验证了自身身份或得到了服务器的授权,客户端将获得读写属性值的权限或是只允许访问属性值的权限。客户端无法显式地获得属性的权限,只能隐式地通过发送请求并且接收错误的响应来尝试获得,该错误响应会说明不能完成请求的原因。
属性协议本身大多是无状态的。每一次事务处理(比如读取请求和读取响应等)并不会让服务器保持其状态,这使得协议本身只需要很少的内存即可工作。不过,仍然有个例外:准备和执行写入请求会将一组数据首先存储在服务器上,然后在一次事务处理中顺序执行所有的操作。

3.2.4 通用属性规范
通用属性规范位于属性协议之上,定义了属性的类型及其使用方法。通用属性规范引入了一些概念,包括“特性”、“服务”、服务之间的“包含”关系、特性“描述符”等。它还定义了一些规程,用来发现服务、特性、服务之间的关系,以及用来读取和写入特性值。
服务是设备上若干原子行为的不可变封装。听上去挺复杂,但却不难理解。不可变意味着一旦服务发布就不能再改变。这一点是必要的,因为若要服务能够被反复使用,最好永远不去动它。一旦服务的行为发生变化,版本号等许多棘手的设置过程和相应配置将耗费大量的时间,这与低功耗蓝牙背后的“无连接模式”的基本概念完全背道而驰。
封装是指简洁地表达事物的功能。一项服务的所有的相关信息都置于属性服务器的一组属性中,并通过其来表达。一旦知道了某个属性服务器上的服务范围,你就知道了服务将封装哪些信息。原子意味着一个更大的系统中单个的不可逆转的单元或部件。原子服务十分重要,这是因为越是小巧的服务越有可能在其他地方获得重用。如果我们创建的服务非常复杂以致包含许多可能相关的行为,那么它们被重用的机会将大大降低。
行为是指事物响应特定情况或刺激的方式。就服务而言,行为意味着当你读取或写入某属性时都发生了些什么,或是什么原因导致了向客户端发送属性通知。明确定义的行为对互操作性尤为重要。如果服务规定的行为含糊不清,每个客户端在与服务器交互时有可能各行其是,服务器的行为表现将取决于哪个客户端正在连接。更糟糕的是,同样的服务在不同的设备上表现也可能大相径庭。一旦设备间出现这种局面,互操作性将被彻底破坏。因此,明确定义的、可测试的行为哪怕交互起来存在错误,仍能提升互操作性。
服务间的关系是实现设备公开的复杂行为的关键。服务本质上是原子的;复杂的行为不应该仅仅通过某一个服务来公开。举个例子来说,一台测量室温的设备可以公开温度服务。设备可能由电池供电,所以它会公开电池服务。然而,如果电池还有一个温度传感器,我们应该在该设备上公开另一个温度服务的实例。第二个温度服务必须和电池相互联系,以便客户端确定其关系,如图3-4所示。


200e4de93f43261ccb7a7a90f6ef2f0cc3eded8a

为了适应复杂的行为和服务之间的关系,服务分为两种类型:首要服务和次要服务。服务的类型通常不取决于服务本身,而取决于设备如何使用该项服务。首要服务从用户角度公开设备的用途;次要服务被首要服务或另一个次要服务使用,使其能够提供完整的行为。在前面的例子中,第一个温度服务是首要服务,电池服务也是一个首要服务,而第二个温度服务—用来表示电池温度的实例则为次要服务,被电池服务所引用。

3.2.5 通用访问规范
通用访问规范定义了设备如何发现、连接,以及为用户提供有用的信息。它还定义了设备之间如何建立长久的关系,称为绑定(bonding)。要启用此功能,规范定义了设备如何实现可发现、可连接和可绑定。还介绍了设备如何使用规程以发现其他设备、连接到其他设备、读取它们的设备名并和它们进行绑定。
通过使用可解析的私有地址,这当中还引入了隐私权的概念。隐私对于那些不断通告其存在以便其他设备能够发现并与之连接的设备而言是非常重要的。然而,希望保留隐私的设备必须采用不断变化的随机地址来广播。这样,其他设备一来不能确定它们侦听的是哪个设备,二来也无法通过跟踪其当前的随机地址判断哪个设备正在周围移动。但是,让受信任的设备能够判断对端是否就在附近并允许其连接,这就要求私有地址必须是可解析的。因此,通用访问规范不仅定义了如何解析私有地址,而且定义了如何与私有设备进行连接。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享: