九、SDP
1. SDP
1.1 服务概述
SDP, Service Discovery Protocol,服务发现协议。
1.1.1 概念
SDP提供了一种用于发现服务及这些可用服务属性的方法,但它不提供利用这些服务的机制。其架构是Client-Server模式,如下图所示:
SDP Server维护了一个服务记录(Service Record)列表,每个条目包含了该服务的信息。
SDP Client通过SDP request来向SDP Server获取服务记录信息
Client可以通过打开一条单独的连接来使用Server提供的某种服务
当Server的服务改变时,Client必须通过其他方式来了解这一信息,以便能够通过SDP来查询 。同时,当Server由于某种原因不可用时,Client可以使用SDP轮询Server
当Server不再响应请求时,Client可以推断该Server不可用。
1.1.2
服务记录(Service Record)
每一个Service用Service Record来表示。
每一个Service Record由若干Service Attribute组成,如下图所示:
在SDP Server中,用Service Record Handle来唯一标识一个Service Record ,而Service Record Handle则使用32-bit数字表示。
在Service Record List中,Server使用0x00000000来表示SDP本身。
1.1.3
服务属性(Service Attribute)
每个服务属性描述了一个服务的单个特征,实例如下:
一个服务属性包含了两个部分: 属性ID和属性值:
属性ID是16-bit无符号整型,用以区分Server中不同属性,属性ID还确定了相关的属性值的语义,属性值字段长度是可变的,由关联属性ID和服务记录类别决定。
1.1.4
服务类(Service Class)
每个服务是一个服务类的实例 ,服务类定义了包含在该类服务记录的所有属性
每个属性定义了指定的属性ID,及使用的属性值和属性值得格式
服务记录包含了特定服务类及通用服务类的属性。
每个服务记录都包含一个唯一的ServiceClassIDList属性,该属性描述了本服务的服务类型。
每个服务类被分配了唯一的标识符 ,这个服务类标识符包含在ServiceClassIDList属性的属性值中,被称为UUID。
下面来看一个实例,下面这个SDP recored来源于ADK的sink例程中的SPP服务:
const uint8 spp_service_record
[SSP_SERVICE_RECORD_SIZE] =
{
0x09, 0x00, 0x01, /* ServiceClassIDList(0x0001)
*/
0x35, 0x03, /* DataElSeq 3 bytes */
0x19, 0x11, 0x01, /* UUID SerialPort(0x1101) */
0x09, 0x00, 0x04, /* ProtocolDescriptorList(0x0004) */
0x35, 0x0c, /* DataElSeq 12 bytes */
0x35, 0x03, /* DataElSeq 3 bytes */
0x19, 0x01, 0x00, /* UUID L2CAP(0x0100) */
0x35, 0x05, /* DataElSeq 5 bytes */
0x19, 0x00, 0x03, /* UUID RFCOMM(0x0003) */
0x08, SPP_DEFAULT_CHANNEL, /* uint8 Suggested RFCOMM
channel for SPP */
0x09, 0x00, 0x09, /* BluetoothProfileDescriptorList(0x0009) */
0x35, 0x06, /* DataElSeq 3 bytes */
0x19, 0x11, 0x01, /* UUID SerialPort(0x1101) */
0x09, 0x01, 0x02, /* SerialPort Version (0x0102) */
0x09, 0x01, 0x00, /* ServiceName(0x0100) = "SPP Dev" */
0x25, 0x07, /* String length 7 */
'S','P','P',' ','D', 'e', 'v'
};
在上面的SDP
Service Record中,ServiceClassIDList属性ID对应的属性值为:SerialPort (0x1101),这就是SPP服务对应的SDP标识符。客户端通过查找该标识符,来获取服务端是否支持SPP服务。其中ServiceClassIDList,ProtocolDescriptorList,L2CAP,RFCOMM,ServiceName是通用服务类属性,SerialPort Version是SPP服务特定的服务属性(私有属性)。某个服务的特定(私有)属性如何查找???。
通常属性ID和属性值封装在不同的数据单元(Data Element)里面,因此属性ID和属性值之间需要插入DataElSeq数据单元,用来记录属性值所占据字节数,这样服务器在解析SDP数据记录时,能够有效定位该属性记录的边界。
上述的SDP记录用jeason表示为:
ServiceClassIDList(0x0001){
SerialPort(0x1101);
}
ProtocolDescriptorList(0x0004){
L2CAP(0x0100){
RFCOMM(0x0003){
RFCOMM_DEFAULT_CHAN;
}
}
}
BluetoothProfileDescriptorList(0x0009){
SerialPort(0x1101);
}
SerialPortVersion;
ServiceName(0x0100){
SERVICENAME_STR;
}
1.1.5
服务查找
服务查找允许Client基于包含服务记录的属性值,来获取特定服务记录的服务记录句柄(Service Record Handle)。 当一个SDP Client有某个服务记录句柄时,它可以请求特定的属性值。SDP不提供基于任意属性值的服务记录查找,只提供基于UUID的查找 。
5.1 UUID
UUID是一个128位的值,蓝牙Base
UUID值为0x00000000-0000-1000-8000-00805F9B34FB ,其他已定义的UUID可参考<UUID>。为了简化实用,我们实用16-bit和32bit UUID来代表真实的UUID。
5.2 服务搜索模式(Service Search
Patterns)
服务搜索模式使用UUID列表来定位匹配的服务记录。
1.1.6
服务浏览
SDP提供了基于服务类共享属性机制来浏览服务,这个属性被称为BrowseGroupList,Client通过创建一个包含代表根浏览组的UUID的服务搜索模式来浏览Server的服务。
1.1.7
数据表示(物理存储形式)
SDP的使用数据单元(Data
Element)来表示数据(属性ID,属性ID范围,属性值),数据单元是一种类型化的数据表示,它由两个字段组成:首部字段(Header Field)和数据字段(Data Field)。
首部字段包含两个部分: 类型描述符(Type
Descriptor)和大小描述符(Size Descriptor) 。
数据字段是一个字节序列,其长度由大小描述符指定,其含义则由类型描述符指定。
7.2类型描述符
数据单元的类型使用5-bit的类型描述符来表示,它包含在首部字段第一个字节的高五位。下面是已经定义的类型:
7.2 大小描述符
数据单元的大小描述符包含在首部字段第一个字节的低三位 ,它表示为的大小指数,其后为0/8/16/32bits ,大小指数的编码如下:
7.3 数据单元实例
1.1.8
协议说明
SDP使用Request/Response模型 ,其中每个事务(Transaction)包含一个请求协议数据单元(PDU)和一个响应PDU,SDP使用L2CAP作为传输协议,在建立连接并发出SDP Request后 在给定的时间内,只有收到该Request的Response后,才能发出其他的Request,传输采用Big-Endian,高位先低位后的方式。
8.1 PDU格式
SDP PDU包含一个Header和Parameters,Header包含三个字段: PDU ID, Transaction ID, ParameterLength。
其中,Header三个字段的含义分别如下:
8.2 Partial Responses And Continuation
State
8.3 错误处理
当一个Server认为Client的Request格式不正确或其他原因导致没有合适的Response时 ,应该回应一个SDP_ErrorResponse
PDU(PDU ID=0x01),同时,其Parameters为ErrorCode,ErrorCode详细信息如下:
8.4 服务查找事务
ServiceSearch Transaction
8.4.1 SDP_ServiceSearchRequest PDU
SDP_ServiceSearchRequest PDU(PDU ID=0x02)的Parameters包括:
ServiceSearchPattern, /*目标服务(UUID标识)列表*/
MaximumServiceRecordCount, /*搜寻最大最大返回服务记录数*/
ContinuationState /*搜索附加的额外参数*/
ServiceSearchPattern(Size:
Varies):
Value |
Parametr |
Data |
ServiceSearchPattern是一个数据单元序列,每个单元是一个UUID,单元数为1~12 |
MaximumServiceRecordCount(Size:
2 Bytes):
Value |
Parametr |
N |
MaximumServiceRecordCount是一个16-bit数,指定可返回的了最大的服务记录句柄,取值范围: 0x0001~0xFFFF |
ContinuationState(Size:
1~17 Bytes):
Value |
Parametr |
Continuation |
ContinuationState是一个8-bit数N,随后的N Bytes是Continuation State信息,N的范围为0~16,0表示没有Continuation State |
8.4.2 SDP_ServiceSearchResponse PDU
SDP_ServiceSearchResponse PDU(PDU ID=0x03)的Parameters包括:
TotalServiceRecordCount, /*搜寻到的服务记录总条目数*/
CurrentServiceRecordCount, /*本次搜寻返回的服务记录条目数*/
ServiceRecordHandleList, /*搜寻到的服务记录句柄列表*/
ContinuationState
/*搜寻返回的额外附属信息*/
8.5 服务属性事务
ServiceAttribute
Transaction
8.6 服务属性查找事务
ServiceSearchAttribute
Transaction
TIP: 8.4.2, 8.5及8.6均为详细定义,此处不累述,详情见规范
1.1.9
服务属性定义
9.1 Universal Attribute Definition
9.2 ServiceDiscoveryServer Service Class
Attribute Definitions
9.3 BrowseGroupDescriptor Service Class
Attribute Definitions
TIP: 9主要描述了属性ID,属性值类型及属性相关说明,详情见规范
上述介绍全部来自:http://www.cnblogs.com/hzl6255/p/3826558.html
关于SDP,更多内容,可参考如下文章 :
<蓝牙的SDP协议总结> http://blog.sina.com.cn/s/blog_69b5d2a50101egbb.html
<SDP协议译稿> http://www.cnblogs.com/strive-forever/archive/2011/11/04/2236640.html
<FTS抓包看蓝牙的SDP整个过程> http://blog.sina.com.cn/s/blog_69b5d2a50101f23c.html
1.2
ADK实现
由于SDP数据记录的维护都封装在BlueCore内,开放给开发人员的东西并不多,因此SDP的实现部分也比较简单。SDP的实现主要涉及到三个库:connect库,SDP parse库,service库,它们之间的关系图如下:
其中SDP Parse库和service库是一些帮助函数,用于帮助解析SDP请求返回的服务数据。重点应该放在Connect库。根据设备所处的角色不同,Connect库分为两大类,服务器端API和客户端API,(当然服务器本身也可以调用查询API获取自身的服务,但是那样使用的场合很少)。
客户端在开始SDP搜索之前,通常需要建立SDP会话(session),这一步骤通常在inquiry阶段完成,因此inquiry阶段完成后,客户端缓存了服务器端部分支持的服务列表(hsp,hfp,a2dp,avrcp)。在后续的通信过程中,如果需要获取服务器是否支持其它某项服务,只需要继续发送下面三个接口即可:
ConnectionSdpServiceSearchRequest();
ConnectionSdpAttributeSearchRequest();
ConnectionSdpServiceSearchAttributeRequest();
在inquiry阶段,如何进行SDP搜索,以及如何保存搜索结果,即后续如何利用这些结果,后面再继续探寻。
SDP模块通信机制与L2CAP,RFCOMM基本类似,这里不再赘述。
九、SDP的更多相关文章
- 如何一步一步用DDD设计一个电商网站(九)—— 小心陷入值对象持久化的坑
阅读目录 前言 场景1的思考 场景2的思考 避坑方式 实践 结语 一.前言 在上一篇中(如何一步一步用DDD设计一个电商网站(八)—— 会员价的集成),有一行注释的代码: public interfa ...
- 谈谈一些有趣的CSS题目(九)-- 巧妙的实现 CSS 斜线
开本系列,谈谈一些有趣的 CSS 题目,题目类型天马行空,想到什么说什么,不仅为了拓宽一下解决问题的思路,更涉及一些容易忽视的 CSS 细节. 解题不考虑兼容性,题目天马行空,想到什么说什么,如果解题 ...
- CRL快速开发框架系列教程九(导入/导出数据)
本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...
- Python(九)Tornado web 框架
一.简介 Tornado 是 FriendFeed 使用的可扩展的非阻塞式 web 服务器及其相关工具的开源版本.这个 Web 框架看起来有些像web.py 或者 Google 的 webapp,不过 ...
- 我的MYSQL学习心得(九) 索引
我的MYSQL学习心得(九) 索引 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据类 ...
- Atitit各种SDM 软件开发过程SDP sdm的ddd tdd bdd设计
Atitit各种SDM 软件开发过程SDP sdm的ddd tdd bdd设计 1.1. software development methodology (also known as SDM 1 1 ...
- 【Oracle 集群】Linux下Oracle RAC集群搭建之基本测试与使用(九)
Oracle 11G RAC数据库安装(九) 概述:写下本文档的初衷和动力,来源于上篇的<oracle基本操作手册>.oracle基本操作手册是作者研一假期对oracle基础知识学习的汇总 ...
- Jsp的九大对象,七大动作,三大指令
jsp九大内置对象:1>out 向客户端输出数据,字节流.如out.print(" dgaweyr"); 2>request 接收客户端的http请求.String g ...
- 今天我们来认识一下JSP的九大内置对象
虽然现在基本上我们都是使用SpringMVC+AJAX进行开发了Java Web了,但是还是很有必要了解一下JSP的九大内置对象的.像request.response.session这些对象,即便使用 ...
随机推荐
- 【腾讯Bugly干货分享】深度学习在OCR中的应用
本文来自于腾讯bugly开发者社区,未经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/5809bb47cc5e52161640c5c8 Dev Club 是一个交流移动 ...
- 【腾讯Bugly干货分享】微信读书iOS性能优化
本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/578c93ca9644bd524bfcabe8 “8小时内拼工作,8小时外拼成长 ...
- javascript 设计模式-----单例模式
单例模式的意思是只需要实例化某个类一次,它的方法也比较简单,通过判断某个类是否已经被实例化了,再返回该值.可以通过各种方法来实现单例模式,下面我们采取以下这种实现方式: var single = (f ...
- Javascript aop(面向切面编程)之around(环绕)
Aop又叫面向切面编程,其中“通知”是切面的具体实现,分为before(前置通知).after(后置通知).around(环绕通知),用过spring的同学肯定对它非常熟悉,而在js中,AOP是一个被 ...
- Linux 比较判断运算(if test)
200 ? "200px" : this.width)!important;} --> 介绍 本篇文章主要是列举在shell命令中常出现的一些用来做比较的运算符,这些运算符是 ...
- C#版的eval,C#Light开源嵌入式脚本,unity热更新不再愁
目前最新版本AlphaV0.06 完全的c#语法,可用于一切能运行C#的场合,wp windows xamarin mono asp.net unity3d 内嵌了int uint bool stri ...
- Java-单例模式(singleton)-转载
概念: java中单例模式是一种常见的设计模式,单例模式分三种:懒汉式单例.饿汉式单例.登记式单例三种. 单例模式有一下特点: 1.单例类只能有一个实例. 2.单例类必须自己自己创建自己的唯一实例. ...
- 大叔也说Xamarin~Android篇~调用远程API接口,发POST请求
回到目录 Xamarin我们在上节已经教大家如何去部署它的环境了,今天来说一个实际的例子,使用android客户调用.net web api的一个接口,并发送POST请求,当服务端回到请求后做出响应, ...
- EF架构~EF6配置需要注意的几个地方
回到目录 从EF5升级到EF6之后,可能会出现一些问题,这是正常的,任何系统的升级都没有一帆风顺的,当然这些不是我要说的重点,我真正要说的是,当出现这些问题时,我们应该如何去应对它,下面我总结了几个方 ...
- [全文检索]Lucene基础入门.
本打算直接来学习Solr, 现在先把Lucene的只是捋一遍. 本文内容: 1. 搜索引擎的发展史 2. Lucene入门 3. Lucene的API详解 4. 索引调优 5. Lucene搜索结果排 ...