八、RFCOMM
1. RFCOMM
先来看看RFCOMM在协议栈层次体系中的位置。从下图可以看出RFCOMM处于传输层。与AVCTP,TCS-BIN处于同一层次。处于其上层的会话层中的OBEX,SPP等大部分协议通常都采用RFCOMM作为传输协议。因此RFCOMM传输协议在蓝牙协议栈中占据重要一席。

RFCOMM提供了基于L2CAP协议的串行(9针RS-232)模拟,支持在两个蓝牙设备间高达60路的通信连接。
1.1 RFCOMM使用示例——SPP
SPP(Serial Port Profile)定义了一系列协议和过程,蓝牙设备通过该协议实现RS232串行线缆的仿真。很多老式设备都工作在串行模块下面,使用SPP协议可以帮组连接这些老式设备。先来卡纳看SPP在蓝牙协议体系里面的位置:

从上图可以看出,SPP协议是大部分应用层协议都采用的会话层协议,常用的GOBEX以及HSP都采用了SPP协议。再来看看SPP协议所处的层次以及服务模型:

SPP按角色分为DevA和DevB,其中DevA是连接的发起者(initiator),DevB是等待连接的到来。从应用层角度看,SPP涉及到三个主要的过程:建立链路并设置虚拟串口连接、接收链路和建立虚拟串口连接、本地SDP数据库注册服务记录,其中第一项对DevA来说是强制实现的,后两项对DevB是强制实现的。
1.1.1 核心过程
1、建立链路并设置虚拟串口连接
该过程包括以下步骤:
1. Submit a query using SDP to find out the RFCOMM Server channel number of the desired application in the remote device. This might include a browsing capability to let the user select among available ports (or services) in the peer device. Or , if it is known exactly which service to contact, it is sufficient look up the necessary parameters using the Service Class ID associated with the desired service.
2. Optionally, require authentication of the remote device to be performed. Also optionally, require encryption to be turned on.
3. Request a new L2CAP channel to the remote RFCOMM entity.
4. Initiate an RFCOMM session on the L2CAP channel.
5. Start a new data link connection on the RFCOMM session, using the aforementioned server channel number.
After step 5, the virtual serial cable connection is ready to be used for communication between applications on both sides.
Note: If there already exists an RFCOMM session between the devices when setting up a new data link connection, the new connection must be established
on the existing RFCOMM.
2、接收链路和建立虚拟串口连接
该过程主要包括以下步骤:
1. If requested by the remote device, take part in authentication procedure and, upon further request, turn on encryption.
2. Accept a new channel establishment indication from L2CAP.
3. Accept an RFCOMM session establishment on that channel.
4. Accept a new data link connection on the RFCOMM session. This may trigger a local request to authenticate the remote device and turn on encryption, if the user has required that for the emulated serial port being connected to (and authentication/encryption procedures have not already been carried out ).
Note: steps 1 and 4 may be experienced as isolated events when there already exists an RFCOMM session to the remote device.
3、本地SDP数据库注册服务记录
该过程涉及到向本地SDP数据库为虚拟串口注册一条服务记录。这也意味着服务数据库的存在,以及对SDP查询的支持。
1.1.2 连接消息序列
先来看看DevB的初始化流程图。上层应用通过调用SppStartService()开始注册和初始化SPP服务,其中涉及到RFCOMM通道注册,SDP服务搜索等交互过程,在上层应用收到SPP_START_SERVICE_CFM表示服务初始化完成,下一步是等待DevA的连接到来。

再来看看典型的连接过程涉及到的消息交互流程图。DevA通过调用SppConnectRequest ()开启连接过程,DevB在接收到SPP_CONNECT_IND消息时决定是否接受该连接,并作出响应SppConnectResponse()。在DevA收到SPP_CLIENT_CONNECT_CFM,DevB收到SPP_SERVER_CONNECT_CFM后,表示SPP通信会话正式建立。

在走读ADK代码时候,你会发现一个奇怪的现象——通信模块(如L2CAP,RFCOMM,SPP等)里找不到数据收发的接口,传统的通信模块,比如SOCKET通信例程中,当通信链路建立后,会提供read/write,read_from/write_to等之类的显示读写函数帮助收发数据。这是因为ADK提供了流的机制,引入sink/source/transform概念。这样,当一条L2CAP/RFCOMM数据链路建立的同时会创建对应的sink实体,用于收发数据。Sink实体在有数据达到或者数据可以接受状态时向已注册任务(task)发送MESSAGE_MORE_DATA/MESSAGE_MORE_SPACE消息。以SPP为例,在DevA收到SPP_CLIENT_CONNECT_CFM,DevB收到SPP_SERVER_CONNECT_CFM后,例程将会保存已创建的sink实体,并进行配置。
Sink sink = (Sink) StreamRfcommSink(cfm->conn_id); /*从RFCOMM会话ID获取sink引用*/
SourceConfigure(StreamSourceFromSink(sink), VM_SOURCE_MESSAGES, VM_MESSAGES_ALL);
其实真正的sink实体创建于L2CAP链路创建成功的时候??这一点还只是猜测。
void connectionHandleL2capConnectCfm(const L2CA_AUTO_CONNECT_CFM_T *cfm){
Sink sink = StreamL2capSink(cfm->cid);
MessageSinkTask(sink, appTask); /* Associate the task with its sink */
}
目前还不清楚sink实体是何时创建的,总之,在SPP建立连接后,就拥有了一个数据收发的sink实体,通过该实体,配合BlueCore发送过来的MESSAGE_MORE_DATA和MESSAGE_MORE_SPACE等消息进行数据传输。
1.1.3 消息处理
SPP库例程中,DevA(Client)定义了一个回调函数——sppcConnectionHandler,用来接收下层的消息,并进行处理和转发。DevB(Server)定义了两个回调函数,一个用来处理连接时候的消息,一个用于处理服务相关(如SDP查询等)的消息。
spp->c.task.handler = sppcConnectionHandler;
spp->c.client_task = theAppTask;
TaskData sppsServiceTask = { sppServiceHandler };
spp->c.client_task = theAppTask;
SppConnectResponse()->
spp->c.task.handler= sppsConnectionHandler;
1.1.4 多串口仿真
两个使用RFCOMM通信的蓝牙设备可以同时打开多个串口仿真 ,RFCOMM支持多大60路,但是一个设备实际能打开的数据依实现而定。
ADK例程中怎么没有发现多串口复用相关的内容呢???
八、RFCOMM的更多相关文章
- 【转】Android bluetooth介绍(二): android blueZ蓝牙代码架构及其uart 到rfcomm流程
原文网址:http://blog.sina.com.cn/s/blog_602c72c50102uzoj.html 关键词:蓝牙blueZ UART HCI_UART H4 HCI L2CAP ...
- Android bluetooth介绍(两): android 蓝牙源架构和uart 至rfcomm过程
关键词:蓝牙blueZ UART HCI_UART H4 HCI L2CAP RFCOMM 版本号:基于android4.2先前版本 bluez内核:linux/linux3.08系统:an ...
- 如何一步一步用DDD设计一个电商网站(八)—— 会员价的集成
阅读目录 前言 建模 实现 结语 一.前言 前面几篇已经实现了一个基本的购买+售价计算的过程,这次再让售价丰满一些,增加一个会员价的概念.会员价在现在的主流电商中,是一个不大常见的模式,其带来的问题是 ...
- iOS可视化动态绘制八种排序过程
前面几篇博客都是关于排序的,在之前陆陆续续发布的博客中,我们先后介绍了冒泡排序.选择排序.插入排序.希尔排序.堆排序.归并排序以及快速排序.俗话说的好,做事儿要善始善终,本篇博客就算是对之前那几篇博客 ...
- 我的MYSQL学习心得(八) 插入 更新 删除
我的MYSQL学习心得(八) 插入 更新 删除 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得( ...
- Mina、Netty、Twisted一起学(八):HTTP服务器
HTTP协议应该是目前使用最多的应用层协议了,用浏览器打开一个网站就是使用HTTP协议进行数据传输. HTTP协议也是基于TCP协议,所以也有服务器和客户端.HTTP客户端一般是浏览器,当然还有可能是 ...
- CRL快速开发框架系列教程八(使用CRL.Package)
本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...
- 【Oracle 集群】Linux下Oracle RAC集群搭建之Oracle DataBase安装(八)
Oracle 11G RAC数据库安装(八) 概述:写下本文档的初衷和动力,来源于上篇的<oracle基本操作手册>.oracle基本操作手册是作者研一假期对oracle基础知识学习的汇总 ...
- 八皇后算法的另一种实现(c#版本)
八皇后: 八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例.该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于 ...
随机推荐
- ASP.NET 5系列教程 (一):领读新特性
近期微软发布了ASP.NET 5.0,本次发布的新特性需求源于大量用户的反馈和需求,例如灵活的跨平台运行时和自主部署能力使ASP.NET应用不再受限于IIS.Cloud-ready环境配置降低了云端部 ...
- 图解集合1:ArrayList
前言 这个分类中,将会写写Java中的集合.集合是Java中非常重要而且基础的内容,因为任何数据必不可少的就是该数据是如何存储的,集合的作用就是以一定的方式组织.存储数据.这里写的集合,一部分是比较常 ...
- Java虚拟机7:内存分配原则
前言 对象的内存分配,往大的方向上讲,就是在堆上分配,少数情况下也可能会直接分配在老年代中,分配的规则并不是百分之百固定的,其细节决定于当前使用的是哪种垃圾收集器组合,当然还有虚拟机中与内存相关的参数 ...
- 第三次作业:PSP耗时
PSP个人项目耗时对比记录表:四则运算 Personal Software Process Stages Time(%) Planning 7 Estimate 9 开发 76 •需求分析 ...
- C#Lite Unity热更新开源解决方案改名C#Light
因为此脚本语言与C#Lite Framework 开源项目重名,故修改名称,发音不变.
- SVM-非线性支持向量机及SMO算法
SVM-非线性支持向量机及SMO算法 如果您想体验更好的阅读:请戳这里littlefish.top 线性不可分情况 线性可分问题的支持向量机学习方法,对线性不可分训练数据是不适用的,为了满足函数间隔大 ...
- 使用finfo_file()函数检测上传图片的类型
该函数可以检测文件的MIME类型.因为有时候我们只根据文件后缀来判断是不准确的. function getMIME($filename){ $finfo = finfo_open(FILEINFO_M ...
- Atitit 深入了解UUID含义是通用唯一识别码 (Universally Unique Identifier),
Atitit 深入了解UUID含义是通用唯一识别码 (Universally Unique Identifier), UUID1 作用1 组成1 全球唯一标识符(GUID)2 UUID 编辑 UUID ...
- paip.mysql 性能测试 报告 home right
paip.mysql 性能测试 报告 home right 作者Attilax 艾龙, EMAIL:1466519819@qq.com 来源:attilax的专栏 地址:http://blog ...
- POI生成WORD文档
h2:first-child, body>h1:first-child, body>h1:first-child+h2, body>h3:first-child, body>h ...