DDS RTPS 协议:模块构成与自动发现
RTPS 规范(The Real-Time Publish-Subscribe Protocol DDS Interoperability Wire Protocol Specification,也称 DDSI-RTPS)定义了 DDS 底层的报文格式与行为,解决的是不同厂商 DDS 产品之间的互操作问题:各家都实现了 DCPS 接口规范,但只有把线上的报文格式和交互行为统一定义,不同实现才能互联互通。本文整理 RTPS 的四大模块(结构、报文、行为、发现),重点展开发现模块的 SPDP/SEDP 两阶段协议与常见实现提供的发现模式。读完可以理解 DDS"纯分布式架构"在协议层是如何落地的。
为什么需要 RTPS
RTPS 产生的因果链:
- DDS 接口(DCPS)规范定义了应用看到的 API 与语义;
- 不同厂家按接口规范实现出各自的 DDS 产品;
- 使用不同 DDS 产品的系统需要集成,对跨厂商互操作、互联互通提出了需求;
- RTPS 规范据此定义 DDS 底层的报文格式、行为等内容。
主流开源 DDS 都基于 RTPS 协议实施:Fast DDS 支持直接调用 RTPS 接口,embeddedRTPS 专注嵌入式场景下的 RTPS 协议实现。
四大模块
RTPS 规范分为四个模块:
| 模块 | 职责 |
|---|---|
| 结构模块 | 定义概念和数据结构 |
| 报文模块 | 定义报文格式 |
| 行为模块 | 定义不同配置下传输的行为,即什么情况下发送什么报文、如何响应接收的报文 |
| 发现模块 | 综合使用以上三个模块,定义简单发现协议,用于通信实体之间的自动发现与匹配 |
结构模块
结构模块描述了作为通信参与者的 RTPS 实体的结构。RTPS 实体是应用程序可见的 DDS 实体相互通信而使用的协议级端点,各概念如下:
| 概念 | 说明 |
|---|---|
| Entity | 实体,包括参与者以及通信实体(写者、读者)的共用属性,包括唯一标识。 |
| Participant | 参与者,作为通信实体(写者、读者)的父实体,存储协议版本、供应商 ID 以及默认的地址信息。 |
| Endpoint | 通信实体,作为写者以及读者的父类抽象,存储两端共有的信息,包括使用的通信地址、可靠性等级以及主题等级。 |
| Writer | 写者,用于发送数据,每个写者关联一个 HistoryCache,用于维护发送的数据,并提供接口发送数据。 |
| Reader | 读者,用于接收数据,每个读者关联一个 HistoryCache,用于维护接收的数据列表。 |
| HistoryCache | 历史数据缓存队列,提供增、删、查的功能,队列中的元素为 CacheChange。 |
| CacheChange | 描述一个样本数据,包括发送方标识、序列号、所属数据实例以及负载。 |
报文模块
报文模块描述了 RTPS Writer 端点和 Reader 端点之间交换的消息的总体结构和逻辑内容:
- Message:RTPS 消息,包含一个消息头(Header)以及 1 个到多个子消息(SubMessage);
- Header:RTPS 消息头;
- SubMessage:子消息,作为不同子消息的抽象结构,包括一个子消息头(SubmessageHeader)以及每种不同子消息各自的一个到多个子消息元素(SubmessageElement)。按功能可以分为三类:
- 可靠型:Heartbeat、AckNack、NackFrag;
- 信息型:InfoDestination、InfoTimeStamp、InfoReply、InfoSource;
- 数据型:Data、Gap、DataFrag;
- 此外还有 Pad(填充)、HeartbeatFrag(分片心跳)等辅助子消息。
行为模块
行为模块进一步定义了 Writer/Reader 的接口及其行为,按可靠性配置对应四类端点:
- StatelessWriter:对应 BEST_EFFORT 类型的数据写者;
- StatelessReader:对应 BEST_EFFORT 类型的数据读者;
- StatefulWriter:对应 RELIABLE 类型的数据写者;
- StatefulReader:对应 RELIABLE 类型的数据读者。
其中 StatelessWriter 的状态机如下:
IDLE 状态下匹配新的远程数据读者后保持 IDLE;有未发送的样本数据时进入 PUSHING 状态,在该状态下循环发送未发送的样本数据(仅涉及 DATA 与 GAP 两类子消息),发送完成后回到 IDLE。
发现模块
发现模块是 DDS"纯分布式架构"的基础:每个应用上的 DDS 实体通过纯分布式的方式获取系统内其他 DDS 实体的信息,并根据这些信息进行基于主题的按需分发。发现模块定义了两个协议:
- 参与者发现协议(PDP):发现网络中其他 DDS 参与者;
- 端点发现协议(EDP):在已发现的参与者之间,发现彼此拥有的、可能匹配的 DataWriter 和 DataReader。
参与者发现协议(PDP)
核心功能:
- 创建内置的参与者发现端点(SPDP Writer/Reader);
- 周期性广播本地参与者信息;
- 接收并处理远程参与者信息;
- 维护参与者代理数据(ParticipantProxyData);
- 触发端点发现过程。
工作流程:
- 启动阶段:每个 DomainParticipant 启动时,创建一对内置端点(SPDP Writer 和 Reader);
- 广播机制:周期性在预定义的多播地址和端口上发送 SPDPdiscoveredParticipantData 消息(周期因实现而异:Fast DDS 默认 3 秒,RTPS 规范建议值为 30 秒);
- 消息内容:包含自身 Participant 的 GUID、QoS、定位信息(Locator,IP/Port)等;
- 接收与处理:接收其他 Participant 的 SPDP 消息,更新参与者代理数据;
- 连接建立:收到新 Participant 公告后,尝试建立与对方 Participant 的通信通道;
- 离开检测:通过停止接收公告(租约超时)或收到明确的 dispose 消息检测 Participant 离开。
发现模式:
- 多播发现:节点加入预定义多播组,无需预知其他节点地址,适用于局域网;
- 单播发现:节点需预先配置对端地址,通过点对点通信交换信息,适用于广域网或跨子网场景。
端点发现协议(EDP)
核心功能:交换 DataWriter 和 DataReader 的信息,匹配可能通信的端点,确认匹配成功后建立数据传输通道。
工作流程:
- 信息交换:通过 PDP 建立的通信信道,共享 DataWriter 和 DataReader 的信息;
- 匹配条件:主题(Topic)和数据类型必须一致;
- 确认机制:通过 Heartbeat、AckNack 等消息确认数据样本。例如 B 的 SEDPbuiltinSubscriptionWriter 发送 Heartbeat 给 A 的 SEDPbuiltinSubscriptionReader,A 收到后若发现缺少样本,会发送 AckNack 告知丢失的样本,B 发送缺失的样本并更新 Heartbeat;
- 匹配完成:当双方确认数据一致后,DataWriter 和 DataReader 可以开始发送/接收用户数据。
实现提供的其他发现机制
以 Fast DDS 为例,除标准发现外还提供多种机制以适应不同应用场景:
- 简单发现(Simple Discovery):默认机制,支持 PDP 和 EDP 的 RTPS 标准,使用 SPDP 和 SEDP 协议自动完成发现过程,与任何其他 DDS 和 RTPS 实现兼容。
- 静态发现(Static Discovery):跳过 EDP 阶段,通过 XML 文件预先配置对端信息,无需自动发现。
- 发现服务器(Discovery Server):集中式发现体系结构,DomainParticipant(称为服务器)充当元流量发现的集线器,适用于大规模分布式系统,可减少网络广播流量。
- 手动发现(Manual Discovery):禁用 PDP,用户使用外部元信息信道手动匹配和取消匹配 RTPS 实体,适用于特殊网络环境或需要高度定制化的系统。
要点
- RTPS 是 DDS 的互操作线协议:统一报文格式与行为,使不同厂商实现可以互通
- 四大模块:结构(协议级端点与 HistoryCache)、报文(Header + SubMessage 三类)、行为(四类端点按可靠性划分)、发现(SPDP/SEDP 两阶段)
- 发现分两步:PDP 发现参与者(周期广播,Fast DDS 默认 3 秒、RTPS 规范建议 30 秒),EDP 在参与者间匹配端点(Topic 与类型一致 + Heartbeat/AckNack 确认)
- 大规模系统可用发现服务器(集中式元流量)或静态发现(XML 预配端点)替代全广播的简单发现