一、介绍
概述
RFCOMM(Radio Frequency Communication Protocol,射频通信协议)是基于ETSI - 07.10 规范的串行线仿真协议,它在蓝牙设备间模拟了传统的串行通信接口,为上层应用提供了简单、易用的串行通信方式。许多应用场景,如文件传输、串口设备连接等,都依赖 RFCOMM 协议。
RFCOMM在蓝牙协议体系中位于中间协议层,通过蓝牙基带仿真传统串行端口控制信号,支持键盘、鼠标等设备的无线连接。早期蓝牙手机与电脑间文件传输、智能传感器与控制器数据传输均依赖该协议实现。

说明:RFCOMM与SPP协议(串口Profile)不同,SPP协议是基于RFCOMM的应用规范,即RFCOMM是协议本身,SPP是RFCOMM的典型应用场景。
RFCOMM是一种简单的传输协议,它额外提供了模拟RS-232(ITU-T V.24)串行端口九个电路的功能。协议支持两个蓝牙设备之间最多同时建立60个并发连接。蓝牙设备中可同时使用的连接数量取决于具体实现。
RFCOMM服务介绍
本介绍内容均基于蓝牙SIG官网的specs中RFCOMM Version1.2来进行介绍,感兴趣的朋友可以自己查阅。
Specifications | Bluetooth® Technology Website
设备类型
对于RFCOMM协议来说,一个完整的通信路径包括运行在两个不同的通信端点上的两个应用程序,应用程序之间有一条通信段,如下图所示:

需要注意的是,“应用程序”并不一定指代用户最终的应用程序APP,还可以指高层协议或是替上层服务跑腿的服务。RFCOMM只管中间这一段,不管两端应用具体在做什么。
虽然在协议层面RFCOMM并不可以区分设备类型,但是想要两种设备都好好工作,协议设计上还是做了一些区分。
- 类型 1 设备:通信的终点,比如电脑、打印机。
- 类型 2 设备:通信路径中间的角色,比如调制解调器(猫)。

字节序
RFCOMM采用与欧洲电信标准协会(ETSI)制定的 GSM 07.10相同的字节序列方式,即小端序。
-从左到右读的时候,二进制数是从最低有效位到最高有效位,也叫做LSB在前,MSB在后。
RS-232控制信号
前面有提到RFCOMM协议提供了模拟RS-232串口九个电路的功能,在RFCOMM spec中有列出下面的表格。

零调制解调器
RFCOMM 是基于 GSM 07.10 的。GSM 07.10 在传输这些非数据信号的时候,根本不区分 DTE 和 DCE——它只传“状态”,不传“身份”。
RFCOMM 里定义的信号对应关系是这样的:

有意思的是:这种传法,当两个同类型设备(比如两台电脑,都是 DTE)连在一起时,RFCOMM 会自动产生一个隐形的零调制解调器效果,说白了RFCOMM 在协议层直接把这个交叉给“模拟”了,所以叫零调制解调器仿真。
虽然没有任何一根零调制解调器线缆能应付所有场景,但 RFCOMM 给的这个方案在大多数情况下都够用。

RFCOMM协议
在大多数系统中,RFCOMM会作为端口驱动程序的一部分,包含一个串口模拟实体(Port Emulation Entity)。下面展示一个RFCOMM是如何适配到一个典型系统的模型。

在该模型中的专业术语描述可以参考下面的表格:

类型与命令
RFCOMM协议一共支持五种帧类型:SAMB、UA、DM、DISC、UIH

SAMB

发起方通过发送 SABM 命令,强制对端进入异步平衡模式(ABM) 并固定使用 1 字节控制头,以此确立双方的对等通信能力;响应方若接受,必须第一时间回复 UA 帧确认,并在接受该命令的瞬间将本 DLC 的发送与接收状态变量全部清零,从而确保新建链路从洁净状态起步——这既完成了“请求-确认”的握手闭环,也通过状态重置避免了新旧连接序列号的残留干扰,为后续 UIH 帧的数据传输铺平了道路。
UA

UA响应帧是RFCOMM建链与断链流程中的肯定回复,其核心语义在于向发起方明确反馈“已收到且接受”SABM(建立请求)或DISC(关闭请求)命令。
DM

DM响应帧核心在于向发起方明确报告:本站点已逻辑脱离该数据链路,且当前处于的断连稳态(Disconnected Mode)——在此模式下,本站拒绝接收并处理任何命令(无论是建链还是参数协商),唯一能打破这种僵局是收到对端发来的SABM命令。
DISC

DISC命令是RFCOMM中用于主动终止已建立链路的控制帧,其核心语义为:发送方通过DISC明确告知对端断线,并要求对端在真正执行断开动作之前,必须先以UA响应确认收到并接受此命令——这里确保了关闭流程的可靠握手,而非粗暴中断;
若该命令发送在DLCI 0(控制通道)上,其语义等同于多路复用器关闭命令(CLD),意味着整条RFCOMM会话即将终结;收到DISC后,接收方须进入逻辑断开模式,后续若再收到任何命令(除SABM外)均不再受理,所有资源释放的主动权自此交还给发起方。
UIH

UIH帧是RFCOMM协议中唯一承载实际用户数据与绝大多数控制命令的主要帧,其核心设计是轻量化与高效率——它既不更新发送/接收状态变量,也不对序列号做任何校验,且未定义任何应答机制,本质上是一种“即发即弃”的无状态传输;正因如此,它在数据链路层异常时可能丢失或重复,RFCOMM本身对此不提供任何恢复保障,完全依赖上层应用自行处理容错。
GSM07.10协议定义了一个专属的控制通道DLCI 0,该通道被用在两个多路复用器(也可以是执行多路复用功能的协议层实体)传输信息。RFCOMM支持以下的几种commands。

不论何时,只要接收到了一个不支持的命令类型,都应该返回一个"None-Supported Command Response(NSC)"。
帧格式
在GSM 07.10的基本帧格式中,本来是有起始标志位(01111101)和结束标志位(01111101)的,但是RFCOMM把他们都去掉了。在RFCOMM里,每个L2CAP帧里恰好装下一个RFCOMM帧,只包含标志中间的那些字段(地址、控制位、长度位、信息、FCS),如下图所示。

大家可以想想这里为什么可以去掉帧头帧尾?如果去掉了,那上层是如何知道RFCOMM数据的开头和结尾在哪?
其实答案就在前面的描述中,因为RFCOMM是基于L2CAP传输的,而每一个L2CAP帧都刚好恰好能装下一个RFCOMM帧,所以在L2CAP层就已经帮你把帧边界搞定了,RFCOMM就没有必要再包一层01111101。
我们按照RFCOMM SPEC的顺序逐个介绍所有的字段。
Address字段
Address字段由一个单独的字节构成,它包含了Server Channel服务通道,方向位D,帧类型位C/R和地址拓展位EA四个部分,如下图所示。

1. Server Channel(5 bits)
考虑到客户端和服务器应用程序可能同时存在于RFCOMM会话的双方,且任一方的客户端均可独立建立连接,因此,RFCOMM协议将GSM07.10中的DLCI字段拆分成了两部分(D+Server Channel)。

服务器应用注册一个RFCOMM服务接口并被分配一个服务通道号,范围是1-30,需要注意的是0和31不应该被注册使用,其在GSM07.10是作为保留位。同时这个通道号也应该被注册在SDP协议的数据库中。
2. D(1bit)

D即Direction的首字母,也就是方向位,对于RFCOMM会话,发起设备(initiator)被赋予方向位 D=1,相对应的,对端设备(Responder)的方向位会被赋值为0。当在一个已经存在的RFCOMM会话中建立一个新的数据链路连接,方向位会与前面提到的Server Channel一起组成DLCI(Data Link Connection Identifier,数据链路连接标识符),在此之后,该DLCI会被用于端点之间双向的所有数据包的传输。
作为对比我们把GSM07.10协议中的DLCI字段引申进来学习:

DLCI是被用于识别单独和用户信息流以及定义TE和MS之间的连接,理应支持多DLCI,但是数量是由具体实现决定的,DLCI应当支持动态分配。
大家有没有想过,在RFCOMM协议中为什么要将DLCI字段拆分成方向位D和Server Channel两个部分?
其实是为了解决冲突,拿以下的场景举例:
假如设备A和设备B上都运行了“串口打印服务(Server Channel = 5)”。客户端想连接对方的打印服务。如果不改,双方都喊着“我要用DLCI 5”,这个号码在两个设备内部都被占用了,连接时绝对数值会撞车。
而RFCOMM中给出的解决版本就能有效规避这一点,其中的关键就是方向位D:
文档原文是这样说的:
- 响应方(D=0) 上的服务器应用,被映射到 偶数 DLCI(2, 4, 6 ... 60)。
- 发起方(D=1) 上的服务器应用,被映射到 奇数 DLCI(3, 5, 7 ... 61)。
计算的公式是:DLCI = (Server Channel × 2) + 对端的D 位
因为你是为“对端设备”上的应用程序建立连接,所以必须使用对端的方向位,而这个值恰好等于你自己方向位的反向(取反)。
3. C/R位(1bit)
在GSM 07.10协议中,存在有两类不同的C/Rbit位,一个位于帧级别(在帧头的地址字段),还有一个是消息系别(在多路复用控制通道上的命令类型字段)。这两个bit位是相互独立的。
先介绍在帧级别的C/R位,在Address帧中C/R位被设置的规则可参照如下:
- 对于SABM、UA、DM、和DISC帧来说,C/R位根据GSM 07.10,5.2.1.2章的表一进行设置。
个人理解就是正向的数据流(Initiator发给Responder Command,Responder 回复 Initialor Response)我们的C/R位就被设置1,反向的数据流C/R位就会被设置为0。

4. EA位(1bit)

对RFCOMM协议来说,EA字段固定为1即可。
Control字段
Control由一个字节(8位)构成,主要用于标识RFCOMM frame type是什么,我们按照SPEC介绍的顺序来进行介绍。

P/F bit(Poll/Final位,bit5)

- 无流控场景:
-
- P=1:Poll 轮询,发送方要求对方应答
- F=1:Final 结束,接收方用来回复轮询请求 普通 UIH 数据帧一般 P/F=0。
- 开启 Credit 基于信用流控后:
P/F 不再做轮询应答,复用该bit携带信用额度相关标识,帧头格式随之改动。
Q:P/F位不是同一个bit位吗?如何判断等于1时表示Poll还是Final?
A:P/F位是同一个bit位,它在不同场景下的含义不同,当该RFCOMM为命令帧时,这个bit位为Poll,当该RFCOMM为响应帧时,这个bit位为Final。
| 0 | 1 | |
| 命令帧(Command) | 不要求回复 | 要求立即回复 |
| 响应帧(Response) | 普通响应 | 回复刚才的P=1请求 |
Length Indicator字段
该字段表示整个帧数据的长度,占用的字节可能位1~2


L1到L7位表示数据字段的长度,其默认值为31字节。同样,它可以根据EA位进行扩展。当EA=1时,表示这就是长度字段的最后一个字节,此时的长度值由表3中的bit2~bit8表示;当EA=0 时,表示后续还有第二个字节,它接下来的字节数如上表3和表4,一共15个bit。
需要注意的是,虽然这里也有EA位,但是与Adrees字段中EA位固定为1不同,这里的EA位是动态的,它是一个"续行标志",用于指示长度字段是否结束,
Information 字段

在RFCOMM中,信息字段就是UIH帧专有的“纯数据载荷区”——对于普通DLC,它直接搬运应用层串行数据;对于控制通道(DLCI 0),它搬运协议控制命令;且由于只用了类型1汇聚层,这里没有任何额外封装,直接就是字节对齐的原始负载。
FCS字段

这里是很容易有坑的地方:SPEC写的很清楚,对于不同帧类型的FCS字段,参与校验计算的字段不同:
- SABM、DISC、UA、和DM帧,FCS字段基于Address、Control和Length字段进行计算;
- 对于UIH帧,仅基于Address和Control帧进行计算。
而且GSM07.10协议7.00版本本身已经修改了对于FCS参与计算字段的定义,但是RFCOMM还是沿用了我们前面说到的旧计算规则,并不跟随新版进行改动!这里只是作为知识补充。
多路复用
一句话:RFCOMM协议本身是支持多路复用能力的(Multiplexer)。而且RFCOMM协议是基于GSM07.10多路复用模型裁剪出来的子集。但是我们往后面看可能会有点懵,感觉是前后矛盾的,我们要如何理解呢?
启动/关闭方式

叽里咕噜说了一大堆,实际上对于RFCOMM协议来说,它并不支持在GSM07.10协议第5.7节中规定的启动和关闭流程!RFCOMM不用 AT+CMUX 来启动 multiplexer即不用 CLD 来关闭 multiplexer。
在 Bluetooth 里,RFCOMM session 的建立不是靠 AT 命令触发,而是依托 Bluetooth 连接和 RFCOMM 自己的 SABM/DISC 等机制来管理。规范在 RFCOMM 1.2, Section 5.2 还明确说:任意两个设备之间最多只有一个 RFCOMM session;如果已经有 session,再开新的 DLC 就复用这个 session。
运行参数

例如 Table 5.1 里的:
- Maximum Frame Size (N1) - Acknowledgement Timer (T1)
- Response Timer for Multiplexer Control Channel (T2)
这些参数不是“AT+CMUX 启动流程”的一部分,而是多路复用时在运行过程中仍然需要的协议参数。 换句话说:虽然 RFCOMM 不按 GSM 07.10 的方式启动/关闭,但它仍然需要一些来自 GSM 07.10 模型的定时器和帧大小参数来工作。
可以把它理解成:
不采用 GSM 07.10 的“开机/关机仪式”,但仍采用这个协议里开机后运行时要用到的“部分参数”。
更直白一点:
- 不支持 AT+CMUX / CLD ≠ 没有 system parameters
RFCOMM 不支持 GSM 07.10 的完整控制入口和退出入口,但支持其中对 RFCOMM 仍然有意义的参数化机制。由于RFCOMM依赖于较低层来提供可靠传输,因此默认设置的超时处理动作是 关闭多路复用会话。
控制命令
控制命令主要在多路控制通道DLCI=0 发送的,其主要用来控制RFCOMM 的连接,来协商一些
参数,主要有以下类型:
• PN—DLC parameter negotiation.
• Test—Checks communication link.
• FCon / FCoff—Aggregate flow control on all connections.
• MSC—Modem status, used for flow control per connection.
• RPN—Remote Port Negotiation.
• RLS—Remote Line Status.
• NSC—Non-Supported Command (response only).
主要基于UIH control frame 来发送,多路控制数据格式如下:

最后总结一下,我们从RFCOMM协议的设计初衷一路走到了帧结构的比特级细节。回顾这段旅程,RFCOMM并非简单照搬GSM 07.10,而是针对蓝牙对等通信(Peer-to-Peer)场景进行了脱胎换骨的改造:
核心矛盾与精妙解耦。GSM 07.10生来为主从架构(DTE-DCE)服务,而蓝牙连接的两端往往是两台地位平等的设备。为此,RFCOMM砍掉了AT+CMUX/CLD等繁杂的启停流程,改用L2CAP通道+SABM命令直接建立会话;更精妙的是,它将地址帧中的绝对DLCI拆解为“方向位(D)+ 服务器通道(1~30)”,让同一服务号在两台设备上映射出奇偶不同的DLCI,完美规避了地址冲突——这是RFCOMM最令人拍案的设计。
极简至上的帧结构。为了在有限的蓝牙带宽上跑出效率,RFCOMM去掉了头尾标志位,固定控制字段为1字节,并通过EA位让长度指示器在1~2字节间弹性伸缩;UIH帧承载一切数据和命令,且FCS仅校验帧头,将校验开销压到最低。
向后兼容与演进。强制支持的信贷流控(Credit Based Flow Control)取代了过时的FCon/FCoff,实现了精细到每个DLC的逐向流量控制,让高速数据传输不再畏手畏脚。

203

被折叠的 条评论
为什么被折叠?



