5. 发现(Discovery)
Fast DDS 作为数据分发服务(Data Distribution Service, DDS)的一种实现,提供了发现机制,能够自动在域参与者(DomainParticipant)之间查找并匹配数据写入器(DataWriter)和数据读取器(DataReader),使它们可开始共享数据。所有发现机制均通过两个阶段实现。
5.1 发现阶段(Discovery Phases)
参与者发现阶段(Participant Discovery Phase, PDP)
在此阶段,各个域参与者(DomainParticipant) 会确认彼此的存在。为实现这一目标,每个域参与者会定期发送通告消息,消息中会明确该域参与者监听元数据和用户数据流量的单播地址(IP 地址和端口)等信息。当两个域参与者处于同一 DDS 域(DDS Domain)时,它们会完成匹配。
默认情况下,通告消息通过知名多播地址和端口(根据域 ID(DomainId) 计算得出)发送。此外,还可指定一个地址列表,通过单播方式发送通告(详见 “初始对等方(Initial peers)” 部分);同时,也能配置此类通告的发送周期(详见 “发现配置(Discovery Configuration)” 部分)。
端点发现阶段(Endpoint Discovery Phase, EDP)
在此阶段,数据写入器(DataWriter) 与数据读取器(DataReader) 会确认彼此的存在。具体过程为:域参与者利用在 PDP 阶段建立的通信通道,相互共享各自数据写入器和数据读取器的相关信息,这些信息包含主题(Topic) 和数据类型(详见 “主题(Topic)” 部分)等内容。
两个端点(数据写入器或数据读取器)要实现匹配,其主题和数据类型必须一致。一旦数据写入器与数据读取器完成匹配,它们便可开始发送 / 接收用户数据流量。
重要提示可在 PDP 阶段传输域参与者运行所在的主机、用户及进程(物理信息)相关信息。有关如何配置待传输物理数据的详细说明,请参考 “发现信息中的物理数据(Physical Data in Discovery Information)” 部分。
5.2 发现机制(Discovery Mechanisms)
Fast DDS 提供以下几种发现机制:
- 简单发现(Simple Discovery):这是默认机制。其 PDP 阶段和 EDP 阶段均遵循实时发布订阅协议(RTPS)标准,因此可与其他任何 DDS 及 RTPS 实现兼容。
- 静态发现(Static Discovery):该机制在 PDP 阶段采用简单参与者发现协议(Simple Participant Discovery Protocol, SPDP,符合 RTPS 标准),但如果提前已知所有数据写入器和数据读取器的 IP 地址、端口、数据类型及主题,便可跳过简单端点发现协议(Simple Endpoint Discovery Protocol, SEDP)阶段。
- 发现服务器(Discovery Server):这种发现机制采用集中式发现架构,其中一个被称为 “服务器(Server)” 的域参与者会作为元数据流量发现的中枢。
- 手动发现(Manual Discovery):该机制仅与 RTPS 层兼容。它会禁用 PDP 阶段,允许用户通过自选的外部元信息通道,手动匹配和解除匹配RTPS 参与者(RTPSParticipants)、RTPS 读取器(RTPSReaders) 和RTPS 写入器(RTPSWriters)。因此,用户需访问由域参与者实现的 RTPS 参与者,并直接对 RTPS 实体进行匹配操作。
5.3.1 通用发现设置(General Discovery Settings)
部分发现设置在不同的发现机制中是通用的。这些设置在WireProtocolConfigQos类的builtin公有数据成员下定义,具体如下:
| 名称(Name) | 描述(Description) | 类型(Type) | 默认值(Default) |
|---|---|---|---|
| 发现协议(Discovery Protocol) | 要使用的发现协议(详见 “发现机制(Discovery mechanisms)” 部分) | DiscoveryProtocol | SIMPLE |
| 忽略参与者标志(Ignore Participant flags) | 对来自同一进程、不同进程或不同主机的域参与者(DomainParticipant)的发现流量进行过滤 | ParticipantFilteringFlags | NO_FILTER |
| 租约时长(Lease Duration) | 远程域参与者将本地域参与者视为 “存活” 状态的时长 | Duration_t | 20 秒 |
| 通告周期(Announcement Period) | 域参与者发送 PDP(参与者发现阶段)通告的周期 | Duration_t | 3 秒 |
5.3.1.1 发现协议(Discovery Protocol)
指定要使用的发现协议(详见 “发现机制(Discovery mechanisms)” 部分)。其可能的取值如下:
| 发现机制(Discovery Mechanism) | 可能的取值(Possible values) | 描述(Description) |
|---|---|---|
| 简单发现(Simple) | SIMPLE | 符合 RTPS 标准的简单发现协议 |
| 发现服务器(Discovery Server) | SERVER | 域参与者作为发现流量的中枢,接收并分发发现信息 |
| - | CLIENT | 域参与者作为发现流量的客户端,将自身发现信息发送至服务器,并仅接收与自身相关的信息 |
| - | SUPER_CLIENT | 域参与者作为发现流量的客户端,将自身发现信息发送至服务器,并接收来自服务器的所有其他发现信息 |
| - | BACKUP | 创建一个带有持久化sqlite数据库的SERVER类型域参与者。备份(BACKUP)服务器可在启动时加载数据库,该类型服务器能提升发现服务器架构在服务器故障时的抗风险能力 |
| 手动发现(Manual) | NONE | 禁用 PDP 阶段,因此不存在 EDP(端点发现阶段)。所有匹配操作必须通过 RTPS 层的addReaderLocator、addReaderProxy、addWriterProxy方法手动完成 |
C++ 代码示例
DomainParticipantQos pqos;
pqos.wire_protocol().builtin.discovery_config.discoveryProtocol =
DiscoveryProtocol::SIMPLE;
XML 配置示例
<?xml version="1.0" encoding="UTF-8" ?>
<dds xmlns="http://www.eprosima.com">
<profiles>
<participant profile_name="participant_discovery_protocol">
<rtps>
<builtin>
<discovery_config>
<discoveryProtocol>SIMPLE</discoveryProtocol>
</discovery_config>
</builtin>
</rtps>
</participant>
</profiles>
</dds>
5.3.1.2 忽略参与者标志(Ignore Participant flags)
定义一个过滤器,用于过滤接收到的部分发现流量。此设置有助于为域参与者提供额外的隔离层级,其可能的取值如下:
| 可能的取值(Possible values) | 描述(Description) |
|---|---|
NO_FILTER | 处理所有发现流量 |
FILTER_DIFFERENT_HOST | 丢弃来自其他主机的发现流量 |
FILTER_DIFFERENT_PROCESS | 丢弃来自同一主机上其他进程的发现流量 |
FILTER_SAME_PROCESS | 丢弃来自域参与者自身所在进程的发现流量 |
FILTER_DIFFERENT_PROCESS | FILTER_SAME_PROCESS | 丢弃来自域参与者自身所在主机的发现流量(注:|为按位或运算符,用于组合多个过滤条件) |
C++ 代码示例
DomainParticipantQos pqos;
pqos.wire_protocol().builtin.discovery_config.ignoreParticipantFlags =
static_cast<eprosima::fastdds::rtps::ParticipantFilteringFlags>(
ParticipantFilteringFlags::FILTER_DIFFERENT_PROCESS |
ParticipantFilteringFlags::FILTER_SAME_PROCESS);
XML 配置示例
<?xml version="1.0" encoding="UTF-8" ?>
<dds xmlns="http://www.eprosima.com">
<profiles>
<participant profile_name="participant_discovery_ignore_flags">
<rtps>
<builtin>
<discovery_config>
<ignoreParticipantFlags>FILTER_DIFFERENT_PROCESS | FILTER_SAME_PROCESS</ignoreParticipantFlags>
</discovery_config>
</builtin>
</rtps>
</participant>
</profiles>
</dds>
注意若需配置域参与者使其不接收来自自身数据写入器(DataWriter)的数据,请参考 “忽略本地端点(Ignore Local Endpoints)” 部分。
5.3.1.3 租约时长(Lease Duration)
该参数指定远程域参与者将本地域参与者视为 “存活” 状态的时长。若在该时长内,本地域参与者的活跃度未得到确认,远程域参与者会将本地域参与者标记为 “已失效”,并删除与本地域参与者及其所有端点相关的所有信息。
每当远程域参与者接收到来自本地域参与者的任何类型流量时,本地域参与者在远程域参与者处的活跃度便会得到确认。
租约时长通过Duration_t类型指定,单位为 “秒(sec)” 和 “纳秒(nanosec)”。
C++ 代码示例
DomainParticipantQos pqos;
pqos.wire_protocol().builtin.discovery_config.leaseDuration = Duration_t(10, 20);
XML 配置示例
<?xml version="1.0" encoding="UTF-8" ?>
<dds xmlns="http://www.eprosima.com">
<profiles>
<participant profile_name="participant_discovery_lease_duration">
<rtps>
<builtin>
<discovery_config>
<leaseDuration>
<sec>10</sec>
<nanosec>20</nanosec>
</leaseDuration>
</discovery_config>
</builtin>
</rtps>
</participant>
</profiles>
</dds>
5.3.1.4 通告周期(Announcement Period)
该参数指定域参与者发送 PDP(参与者发现阶段)通告的周期。从活跃度保障角度出发,建议通告周期短于租约时长,这样即使在无数据流量的情况下,域参与者的活跃度也能得到确认。
需要注意的是,通告周期的设置存在权衡关系:过于频繁的通告会导致网络中充斥元数据流量,而过于稀疏的通告则会延迟 “后期加入者”(指在系统运行过程中新增的域参与者)的发现过程。
域参与者的通告周期通过Duration_t类型指定,单位为 “秒(sec)” 和 “纳秒(nanosec)”。
C++ 代码示例
DomainParticipantQos pqos;
pqos.wire_protocol().builtin.discovery_config.leaseDuration_announcementperiod = Duration_t(1, 2);
XML 配置示例
<?xml version="1.0" encoding="UTF-8" ?>
<dds xmlns="http://www.eprosima.com">
<profiles>
<participant profile_name="participant_discovery_lease_announcement">
<rtps>
<builtin>
<discovery_config>
<leaseAnnouncement>
<sec>1</sec>
<nanosec>2</nanosec>
</leaseAnnouncement>
</discovery_config>
</builtin>
</rtps>
</participant>
</profiles>
</dds>
5.3.2 简单发现设置(SIMPLE Discovery Settings)
简单发现协议(SIMPLE Discovery Protocol)用于实现各类 DDS 实体(DDS Entities)间端到端连接的建立。eProsima Fast DDS 通过实现该协议,确保与 RTPS 标准的兼容性。该协议规范将简单发现协议拆分为两个相互独立的协议:
- 简单参与者发现协议(Simple Participant Discovery Protocol, SPDP):定义域参与者(DomainParticipant)如何在网络中发现彼此,用于通告并检测同一域内域参与者的存在。
- 简单端点发现协议(Simple Endpoint Discovery Protocol, SEDP):定义已发现的域参与者之间交换信息的规则,目的是发现各自包含的 DDS 实体(即 DataWriter 数据写入器和 DataReader 数据读取器)。
| 名称(Name) | 描述(Description) |
|---|---|
| 初始通告(Initial Announcements) | 定义域参与者初始通告的行为 |
| 简单 EDP 属性(Simple EDP Attributes) | 定义将 SIMPLE 协议用作发现协议的相关配置 |
| 初始对等方(Initial peers) | 发送 SPDP 通告的域参与者 IP / 端口对列表 |
5.3.2.1 初始通告(Initial Announcements)
RTPS 标准中的简单发现机制要求域参与者发送通告,告知自身在域内的存在。这类通告并非可靠传输,可能会被网络丢弃。为避免因消息丢弃导致的发现延迟,可对初始通告进行配置,使其多次发送,以提高消息被正常接收的概率(详见InitialAnnouncementConfig)。
初始通告仅在参与者创建时执行。此阶段结束后,仅会按照leaseDuration_announcementperiod周期(而非下文的period参数)发送标准通告。
| 名称(Name) | 描述(Description) | 类型(Type) | 默认值(Default) |
|---|---|---|---|
| count(次数) | 定义启动时发送的通告数量 | uint32_t | 5 |
| period(周期) | 定义初始通告的特定发送周期 | Duration_t | 100 毫秒 |
C++ 代码示例
DomainParticipantQos pqos;
pqos.wire_protocol().builtin.discovery_config.initial_announcements.count = 5;
pqos.wire_protocol().builtin.discovery_config.initial_announcements.period = Duration_t(0, 100000000u);
XML 配置示例
<participant profile_name="participant_profile_simple_discovery">
<rtps>
<builtin>
<discovery_config>
<initialAnnouncements>
<count>5</count>
<period>
<nanosec>100000000</nanosec>
</period>
</initialAnnouncements>
</discovery_config>
</builtin>
</rtps>
</participant>
5.3.2.2 简单 EDP 属性(Simple EDP Attributes)
| 名称(Name) | 描述(Description) | 类型(Type) | 默认值(Default) |
|---|---|---|---|
| SIMPLE EDP | 定义在 EDP 阶段是否使用 SIMPLE 协议作为发现协议。一个域参与者可创建 DataWriter、DataReader、两者皆创建或两者皆不创建 | bool | true |
| Publication writer and Subscription reader(发布写入器与订阅读取器) | 适用于仅实现一个或多个 DataWriter(即不实现 DataReader)的域参与者,仅允许创建与 DataReader 发现相关的 EDP 端点 | bool | true |
| Publication reader and Subscription writer(发布读取器与订阅写入器) | 适用于仅实现一个或多个 DataReader(即不实现 DataWriter)的域参与者,仅允许创建与 DataWriter 发现相关的 EDP 端点 | bool | true |
C++ 代码示例
DomainParticipantQos pqos;
pqos.wire_protocol().builtin.discovery_config.use_SIMPLE_EndpointDiscoveryProtocol = true;
pqos.wire_protocol().builtin.discovery_config.m_simpleEDP.use_PublicationWriterANDSubscriptionReader = true;
pqos.wire_protocol().builtin.discovery_config.m_simpleEDP.use_PublicationReaderANDSubscriptionWriter = false;
XML 配置示例
<participant profile_name="participant_profile_qos_discovery_edp">
<rtps>
<builtin>
<discovery_config>
<EDP>SIMPLE</EDP>
<simpleEDP>
<PUBWRITER_SUBREADER>true</PUBWRITER_SUBREADER>
<PUBREADER_SUBWRITER>false</PUBREADER_SUBWRITER>
</simpleEDP>
</discovery_config>
</builtin>
</rtps>
</participant>
5.3.2.3 初始对等方(Initial peers)
根据 RTPS 标准(第 9.6.1.1 节),每个RTPSParticipant(RTPS 参与者)必须监听两个不同端口的参与者发现协议(PDP)元流量:一个端口关联多播地址,另一个端口关联单播地址。
Fast DDS 允许配置初始对等方列表,该列表包含一个或多个 IP - 端口对,对应远程域参与者用于监听 PDP 发现的资源。本地域参与者仅会向初始对等方列表中指定的 IP - 端口对发送 PDP 流量。
域参与者的初始对等方列表包含其将通信的所有其他域参与者的 IP - 端口对,既可包含多播地址,也可包含单播地址,是域参与者在 PDP 发现机制中使用的地址列表。若列表为空,则使用默认多播地址。因此,该配置方式也适用于不支持多播功能的场景。
根据 RTPS 标准(第 9.6.1.1 节),RTPS 参与者监听发现流量的单播端口通过以下公式计算:7400 + 250 * 域ID(domainID) + 10 + 2 * 参与者ID(participantID)。例如,若某个 RTPS 参与者在域 0(默认域)中运行,且其 ID 为 1,则其监听发现流量的单播端口为:7400 + 250 * 0 + 10 + 2 * 1 = 7412。
默认情况下,eProsima Fast DDS 将元流量多播定位器(Metatraffic Multicast Locators)用作初始对等方。
以下示例配置了一个初始对等方列表,列表中包含一个对等方:主机 IP 为 192.168.10.13,域 0 中的域参与者 ID 为 1。
注意也可选择不定义初始对等方的端口。在此情况下,发现信息将发送到从参与者 ID 0 到
TransportDescriptorInterface中设置的maxInitialPeersRange值之间的所有端口。因此,将maxInitialPeersRange值设置为至少等于预期的最大域参与者数量,可确保实现正常的发现与通信。
C++ 代码示例
DomainParticipantQos qos;
// 配置主机192.168.10.13上的初始对等方
// 端口号对应域0中参与者ID为1的元流量单播知名端口
Locator_t initial_peer;
IPLocator::setIPv4(initial_peer, "192.168.10.13");
initial_peer.port = 7412;
qos.wire_protocol().builtin.initialPeersList.push_back(initial_peer);
XML 配置示例
<!-- <?xml version="1.0" encoding="UTF-8" ?>
<profiles xmlns="http://www.eprosima.com"> -->
<participant profile_name="initial_peers_example_profile" is_default_profile="true">
<rtps>
<builtin>
<initialPeersList>
<locator>
<udpv4>
<address>192.168.10.13</address>
<port>7412</port>
</udpv4>
</locator>
</initialPeersList>
</builtin>
</rtps>
</participant>
5.3.3 静态发现设置(STATIC Discovery Settings)
Fast DDS 允许在 EDP(端点发现阶段)中用静态版本替代 SEDP(简单端点发现协议),从而完全消除 EDP 元流量。在网络带宽有限且 DataWriter(数据写入器)与 DataReader(数据读取器)的架构已知的场景下,这种方式十分实用。若所有 DataWriter、DataReader 及其对应的 Topic(主题)和数据类型均已提前确定,那么 EDP 阶段可替换为对等方的静态配置。
需重点注意的是,采用此方式后,不会生成任何 EDP 发现元流量,且仅配置中定义的对等方能够进行通信。静态发现相关设置如下:
| 名称(Name) | 描述(Description) |
|---|---|
| 静态 EDP(STATIC EDP) | 激活静态发现协议 |
| 静态 EDP XML 配置规范(STATIC EDP XML Configuration Specification) | 指定包含远程 DataWriter 和 DataReader 描述信息的 XML 内容 |
| 初始通告(Initial Announcements) | 定义域参与者初始通告(PDP 阶段)的行为 |
5.3.3.1 静态 EDP(STATIC EDP)
要激活静态 EDP,需在 WireProtocolConfigQos 中禁用 SEDP。可通过代码或 XML 配置文件实现:
C++ 代码示例
DomainParticipantQos pqos;
pqos.wire_protocol().builtin.discovery_config.use_SIMPLE_EndpointDiscoveryProtocol = false;
pqos.wire_protocol().builtin.discovery_config.use_STATIC_EndpointDiscoveryProtocol = true;
XML 配置示例
<participant profile_name="participant_profile_static_edp">
<rtps>
<builtin>
<discovery_config>
<EDP>STATIC</EDP>
</discovery_config>
</builtin>
</rtps>
</participant>
当前支持两种参与者发现阶段(PDP)的信息交换格式:默认格式,以及一种能减少网络带宽占用的格式。关于如何切换格式,可参考《静态发现的交换格式》(Static Discovery’s Exchange Format)。
5.3.3.2 静态 EDP XML 配置规范(STATIC EDP XML Configuration Specification)
激活静态 EDP 后,所有 EDP 元流量都会被屏蔽,因此必须通过专用 XML 文件静态指定远程实体(DataWriter 和 DataReader)的信息。一个域参与者(DomainParticipant)可加载多个此类配置文件:既可以将不同实体的信息集中存储在一个文件中,也可以拆分到多个文件中,以保证配置的条理性。Fast DDS 提供了一个静态发现示例(Static Discovery example),实现了该 EDP 发现协议。
下表描述了静态 EDP XML 配置文件的所有可能元素。完整的配置文件示例可参考《静态 EDP XML 示例》(STATIC EDP XML Example)。
| 名称(Name) | 描述(Description) | 取值(Values) | 默认值(Default) |
|---|---|---|---|
<userId> | 必填项。唯一标识 DataReader/DataWriter | uint16_t | 0 |
<entityID> | DataReader/DataWriter 的实体 ID | uint16_t | 0 |
<expects_inline_qos> | 指示是否期望 QoS(服务质量)内联(仅适用于 DataReader) | bool | false |
<topicName> | 必填项。远程 DataReader/DataWriter 的主题,需与本地 DataReader/DataWriter 的某个主题匹配 | string_255 | - |
<topicDataType> | 必填项。主题对应的数据类型 | string_255 | - |
<topicKind> | 主题类型 | NO_KEY(无键)、WITH_KEY(有键) | NO_KEY |
<partitionQos> | 远程对等方的分区名称,可重复配置多个分区 | string | - |
<unicastLocator> | 域参与者的单播定位器,详见《定位器定义》(Locators definition) | - | - |
<multicastLocator> | 域参与者的多播定位器,详见《定位器定义》(Locators definition) | - | - |
<reliabilityQos> | 详见《可靠性 QoS 策略》(ReliabilityQosPolicy)部分 | BEST_EFFORT_RELIABILITY_QOS(尽力而为可靠性)、RELIABLE_RELIABILITY_QOS(可靠可靠性) | BEST_EFFORT_RELIABILITY_QOS |
<durabilityQos> | 详见《持久性 QoS 策略》(DurabilityQosPolicy)部分 | VOLATILE_DURABILITY_QOS(易失性持久化)、TRANSIENT_LOCAL_DURABILITY_QOS(本地瞬态持久化)、TRANSIENT_DURABILITY_QOS(瞬态持久化) | VOLATILE_DURABILITY_QOS |
<ownershipQos> | 详见《所有权 QoS》(Ownership QoS) | - | - |
<livelinessQos> | 定义远程对等方的活跃度,详见《活跃度 QoS》(Liveliness QoS) | - | - |
<disablePositiveAcks> | 详见《禁用确认应答 QoS 策略》(DisablePositiveACKsQosPolicy) | 详见《禁用确认应答》(DisablePositiveAcks) | - |
5.3.3.2.1 定位器定义(Locators definition)
远程对等方的定位器通过 <unicastLocator> 和 <multicastLocator> 标签配置,这两个标签本身不包含值,需通过子标签定义定位器信息。通过 <unicastLocator> 和 <multicastLocator> 定义的定位器具有累积性,可重复配置以给同一对等方分配多个远程端点定位器。
address:必填项,表示定位器地址的字符串(string)。port:可选,标识地址对应的端口,类型为uint16_t。
5.3.3.2.2 所有权 QoS(Ownership QoS)
主题的所有权可通过 <ownershipQos> 标签配置,该标签本身不包含值,需通过子标签配置:
kind:取值为SHARED_OWNERSHIP_QOS(共享所有权)或EXCLUSIVE_OWNERSHIP_QOS(独占所有权),是该标签下的必填子元素。strength:可选,指定远程域参与者对主题的所有权强度,类型为uint32_t,仅适用于 DataWriter。未指定时默认值为 0。
5.3.3.2.3 活跃度 QoS(Liveliness QoS)
远程对等方的活跃度 QoS 策略(LivelinessQosPolicy)通过 <livelinessQos> 标签配置,该标签本身不包含值,需通过子标签配置:
kind:取值为AUTOMATIC_LIVELINESS_QOS(自动活跃度)、MANUAL_BY_PARTICIPANT_LIVELINESS_QOS(按参与者手动活跃度)或MANUAL_BY_TOPIC_LIVELINESS_QOS(按主题手动活跃度),是该标签下的必填子元素。leaseDuration_ms:可选,指定远程对等方的租约时长,类型为uint32。可使用特殊值INF表示无限租约时长,未指定时默认值为INF。
5.3.3.3 检查静态 EDP XML 文件(Checking STATIC EDP XML Files)
加载静态 EDP XML 文件前,建议先检查文件的有效性,确保其能成功加载。可通过 DomainParticipantFactory(域参与者工厂)的 DomainParticipantFactory::check_xml_static_discovery() 方法进行验证,支持通过 XML 文件或直接传入配置内容两种方式,示例如下:
通过文件验证(File)
// (file://)前缀为可选
std::string file = "file://static_Discovery.xml";
DomainParticipantFactory* factory = DomainParticipantFactory::get_instance();
if (RETCODE_OK != factory->check_xml_static_discovery(file))
{
std::cout << "Error parsing xml file " << file << std::endl;
}
通过直接传入配置数据验证(Data)
// 必须使用(data://)前缀以直接加载配置内容
std::string fileData = "data://<?xml version=\"1.0\" encoding=\"utf-8\"?>" \
"<staticdiscovery>" \
"<participant>" \
"<name>HelloWorldPublisher</name>" \
"<writer>" \
"<userId>1</userId>" \
"<entityID>2</entityID>" \
"<topicName>HelloWorldTopic</topicName>" \
"<topicDataType>HelloWorld</topicDataType>" \
"</writer>" \
"</participant>" \
"</staticdiscovery>";
if (RETCODE_OK != factory->check_xml_static_discovery(fileData))
{
std::cout << "Error parsing xml file data:" << std::endl << fileData << std::endl;
}
5.3.3.3.1 静态 EDP XML 示例(STATIC EDP XML Example)
以下是包含两个远程域参与者(一个 DataWriter 和一个 DataReader)的完整 XML 配置示例。该配置必须与创建远程 DataReader/DataWriter 时使用的配置一致,否则可能影响 DataReader 与 DataWriter 之间的通信。若缺少非必填元素,将使用默认值。通常建议:远程 DataReader/DataWriter 创建时指定的所有元素,均需在配置中对应设置。
<staticdiscovery>
<participant>
<name>HelloWorldSubscriber</name>
<reader>
<userId>3</userId>
<entityID>4</entityID>
<expects_inline_qos>true</expects_inline_qos>
<topicName>HelloWorldTopic</topicName>
<topicDataType>HelloWorld</topicDataType>
<topicKind>WITH_KEY</topicKind>
<partitionQos>HelloPartition</partitionQos>
<partitionQos>WorldPartition</partitionQos>
<unicastLocator address="192.168.0.128" port="5000"/>
<unicastLocator address="10.47.8.30" port="6000"/>
<multicastLocator address="239.255.1.1" port="7000"/>
<reliabilityQos>BEST_EFFORT_RELIABILITY_QOS</reliabilityQos>
<durabilityQos>VOLATILE_DURABILITY_QOS</durabilityQos>
<ownershipQos kind="SHARED_OWNERSHIP_QOS"/>
<livelinessQos kind="AUTOMATIC_LIVELINESS_QOS" leaseDuration_ms="1000"/>
<disablePositiveAcks>
<enabled>true</enabled>
</disablePositiveAcks>
</reader>
</participant>
<participant>
<name>HelloWorldPublisher</name>
<writer>
<unicastLocator address="192.168.0.120" port="9000"/>
<unicastLocator address="10.47.8.31" port="8000"/>
<multicastLocator address="239.255.1.1" port="7000"/>
<userId>5</userId>
<entityID>6</entityID>
<topicName>HelloWorldTopic</topicName>
<topicDataType>HelloWorld</topicDataType>
<topicKind>WITH_KEY</topicKind>
<partitionQos>HelloPartition</partitionQos>
<partitionQos>WorldPartition</partitionQos>
<reliabilityQos>BEST_EFFORT_RELIABILITY_QOS</reliabilityQos>
<durabilityQos>VOLATILE_DURABILITY_QOS</durabilityQos>
<ownershipQos kind="SHARED_OWNERSHIP_QOS" strength="50"/>
<livelinessQos kind="AUTOMATIC_LIVELINESS_QOS" leaseDuration_ms="1000"/>
<disablePositiveAcks>
<enabled>true</enabled>
<duration>
<sec>300</sec>
</duration>
</disablePositiveAcks>
</writer>
</participant>
</staticdiscovery>
5.3.3.4 加载静态 EDP XML 文件(Loading STATIC EDP XML Files)
通过静态发现的远程 DataReader/DataWriter,必须在其配置文件中定义唯一的 userID,且该值需与发现配置 XML 中指定的 userId 一致。可通过设置 DataReaderQos/DataWriterQos 实现:
C++ 代码示例
// 配置 DataWriter
DataWriterQos wqos;
wqos.endpoint().user_defined_id = 1;
// 配置 DataReader
DataReaderQos rqos;
rqos.endpoint().user_defined_id = 3;
XML 配置示例
<data_writer profile_name="writer_xml_conf_static_discovery">
<userDefinedID>3</userDefinedID>
</data_writer>
<data_reader profile_name="reader_xml_conf_static_discovery">
<userDefinedID>5</userDefinedID>
</data_reader>
在本地域参与者中,可通过指定文件路径加载静态 EDP 配置内容:
C++ 代码示例(通过文件加载)
DomainParticipantQos pqos;
pqos.wire_protocol().builtin.discovery_config.static_edp_xml_config("file://RemotePublisher.xml");
pqos.wire_protocol().builtin.discovery_config.static_edp_xml_config("file://RemoteSubscriber.xml");
XML 配置示例(通过文件加载)
<participant profile_name="participant_profile_static_load_xml">
<rtps>
<builtin>
<discovery_config>
<static_edp_xml_config>file://RemotePublisher.xml</static_edp_xml_config>
<static_edp_xml_config>file://RemoteSubscriber.xml</static_edp_xml_config>
</discovery_config>
</builtin>
</rtps>
</participant>
也可直接指定静态 EDP 配置内容:
C++ 代码示例(直接指定配置内容)
DomainParticipantQos pqos;
pqos.wire_protocol().builtin.discovery_config.static_edp_xml_config(
"data://<?xml version=\"1.0\" encoding=\"utf-8\"?>" \
"<staticdiscovery><participant><name>RTPSParticipant</name></participant></staticdiscovery>");
5.3.4 发现服务器设置(Discovery Server Settings)
该机制基于客户端 - 服务器(C/S)发现模式,即元流量(域参与者之间用于识别彼此的消息交换)由一个或多个服务器端域参与者(Server DomainParticipant)管理(如左侧图示);与之不同,简单发现(Simple Discovery,右侧图示)通过 IP 多播等消息广播机制交换元流量。目前提供一款发现服务器工具(Discovery-Server tool),可简化发现服务器的配置与测试流程。

注意启用发现服务器机制后,DDS 域(DDS Domain)概念不再适用。
5.3.4.1 核心概念(Key concepts)
要理解该架构,需掌握以下核心概念:
- 协议与实体复用:发现服务器机制复用 RTPS 发现消息结构,以及标准 DDS 的 DataWriter(数据写入器)和 DataReader(数据读取器)。
- 角色划分:发现服务器的域参与者分为客户端(Client)和服务器(Server)两类。二者的唯一区别是处理发现流量的方式不同;而它们创建的 DataWriter 与 DataReader 之间的用户流量(User Traffic),与角色无关。
- 服务器(SERVER)
- 定义:客户端(或其他服务器)向其发送发现信息的参与者。
- 核心作用:将客户端的发现信息分发给已知的客户端和服务器;同时,服务器会向已知服务器通告新服务器的存在(反之亦然)—— 因此,新服务器只需知晓网络中任意一个现有服务器,即可与所有其他现有服务器建立连接。
- 信息来源:分发的发现信息既可能来自直接连接的客户端,也可能来自其他服务器转发的其客户端信息。
- 信息分发规则:
- 已知服务器:接收该服务器直接客户端的所有信息,以及其他服务器的参与者信息(用于通告新服务器)。
- 已知客户端:仅接收建立通信所需的信息(即与客户端匹配的域参与者、DataWriter 和 DataReader 信息)—— 这意味着服务器会运行 “匹配算法”,筛选出每个客户端所需的信息。
- 备份服务器(BACKUP)
- 定义:将发现数据库持久化到文件中的服务器。
- 特性:启动时可从文件加载网络拓扑图,无需接收任何客户端信息;能在服务器运行期间持久化网络认知,避免意外关闭导致信息丢失。
- 注意事项:由于需定期向文件写入数据(开销较大),使用备份服务器会对发现速度产生负面影响。
- 客户端(CLIENT)
- 定义:连接到一个或多个服务器,仅从服务器接收与匹配端点建立通信所需发现信息的参与者。
- 前置要求:需提前知晓待连接服务器的定位器列表(包含服务器监听的 IP 地址、端口,以及客户端与服务器通信的传输协议 ——UDP 或 TCP)。
- 超级客户端(SUPER_CLIENT)
- 定义:接收服务器已知的所有发现信息的客户端(区别于仅接收所需信息的普通客户端)。
- 注意事项:超级客户端不具备服务器功能 —— 仅通过连接的服务器接收信息,不连接其他服务器,也不转发接收的信息;且服务器发现的无端点域参与者,超级客户端无法知晓。
- 通信逻辑:
- 服务器无需提前知晓客户端,但必须在客户端已知的定位器指定地址上监听。
- 客户端会定期(按 ping 周期)向服务器发送发现消息,直到收到消息确认应答;此后服务器会记录该客户端,并向其推送相关发现信息。
- 服务器之间的连接,同样遵循上述逻辑。
5.3.4.2 客户端与服务器的选择(Choosing between Client and Server)
通过 “发现协议” 通用设置(Discovery Protocol general setting)指定参与者角色。一个参与者仅能承担一种角色(尽管服务器可连接其他服务器)。该参数必须手动设置(默认值为SIMPLE)。以下示例展示如何通过代码和 XML 配置该参数:
C++ 代码示例
DomainParticipantQos pqos;
// 设置为客户端
pqos.wire_protocol().builtin.discovery_config.discoveryProtocol = DiscoveryProtocol::CLIENT;
// 设置为超级客户端
pqos.wire_protocol().builtin.discovery_config.discoveryProtocol = DiscoveryProtocol::SUPER_CLIENT;
// 设置为服务器
pqos.wire_protocol().builtin.discovery_config.discoveryProtocol = DiscoveryProtocol::SERVER;
// 设置为备份服务器
pqos.wire_protocol().builtin.discovery_config.discoveryProtocol = DiscoveryProtocol::BACKUP;
XML 配置示例
<?xml version="1.0" encoding="UTF-8" ?>
<profiles xmlns="http://www.eprosima.com">
<participant profile_name="participant_discovery_protocol_alt" >
<rtps>
<builtin>
<discovery_config>
<discoveryProtocol>CLIENT</discoveryProtocol>
<!-- 其他可选值
<discoveryProtocol>SERVER</discoveryProtocol>
<discoveryProtocol>SUPER_CLIENT</discoveryProtocol>
<discoveryProtocol>BACKUP</discoveryProtocol>
-->
</discovery_config>
</builtin>
</rtps>
</participant>
</profiles>
5.3.4.3 服务器定位器列表(The server locator list)
- 每个服务器必须指定可被访问的有效定位器。
- 每个客户端必须获取连接服务器所需的正确定位器。
以下分别展示服务器端与客户端的配置示例:
5.3.4.3.1 服务器端配置(Server side setup)
示例展示如何配置服务器定位器列表及对应的 XML 标签。每个定位器需包含:IP 地址、端口、传输协议(UDPv4/6 或 TCPv4/6)。
C++ 代码示例
Locator_t locator;
// 默认定位器类型为UDPv4,也可手动指定
locator.kind = LOCATOR_KIND_UDPv4;
// 设置IPv4地址(示例:192.168.1.133)
IPLocator::setIPv4(locator, 192, 168, 1, 133);
// 设置端口
locator.port = 64863;
// 配置服务器QoS
DomainParticipantQos serverQos;
// 将定位器添加到元流量单播定位器列表
serverQos.wire_protocol().builtin.metatrafficUnicastLocatorList.push_back(locator);
XML 配置示例
<?xml version="1.0" encoding="UTF-8" ?>
<profiles xmlns="http://www.eprosima.com">
<participant profile_name="participant_profile_discovery_server_server_metatraffic">
<rtps>
<builtin>
<!-- 元流量单播定位器列表 -->
<metatrafficUnicastLocatorList>
<locator>
<udpv4>
<!-- 服务器UDP地址占位符 -->
<address>192.168.1.113</address>
<port>64863</port>
</udpv4>
</locator>
</metatrafficUnicastLocatorList>
</builtin>
</rtps>
</participant>
</profiles>
注意服务器可连接其他服务器,因此下一节(客户端配置)的部分逻辑也适用于服务器间连接。
5.3.4.3.2 客户端配置(Client side setup)
每个客户端需维护待连接服务器的定位器列表。
注意若提供的定位器不可达,客户端会定期向该地址发送 ping 消息,直到连接到定位器列表中配置的所有服务器。
C++ 代码示例
Locator_t locator;
// 默认定位器类型为UDPv4
locator.kind = LOCATOR_KIND_UDPv4;
// 设置服务器IPv4地址(示例:192.168.1.133)
IPLocator::setIPv4(locator, 192, 168, 1, 133);
// 设置服务器端口
locator.port = 64863;
// 配置客户端QoS
DomainParticipantQos clientQos;
// 将服务器定位器添加到发现服务器列表
clientQos.wire_protocol().builtin.discovery_config.m_DiscoveryServers.push_back(locator);
XML 配置示例
<?xml version="1.0" encoding="UTF-8" ?>
<profiles xmlns="http://www.eprosima.com">
<participant profile_name="participant_profile_discovery_server_client_metatraffic">
<rtps>
<builtin>
<discovery_config>
<!-- 发现服务器列表 -->
<discoveryServersList>
<locator>
<udpv4>
<!-- 服务器UDP地址占位符 -->
<address>192.168.1.113</address>
<port>64863</port>
</udpv4>
</locator>
</discoveryServersList>
</discovery_config>
</builtin>
</rtps>
</participant>
</profiles>
注意定位器中可指定逻辑端口(Logical Port)。若未指定,Fast DDS 会在需要时自动将逻辑端口设为与物理端口相同的值。该行为与
ROS_DISCOVERY_SERVER环境变量及 Fast DDS 命令行工具(Fast DDS CLI tool)的实现逻辑一致。
5.3.4.4 发现服务器握手的精细调整(Fine tuning discovery server handshake)
如前文所述,客户端会定期(按 ping 周期)向服务器发送发现消息,直到收到与指定远程定位器(服务器地址)数量相同的确认应答。服务器之间的连接也遵循该 ping 周期规则。
默认 ping 周期为 450 毫秒,可通过以下方式修改:
C++ 代码示例
DomainParticipantQos participant_qos;
// 设置ping周期为250毫秒(参数单位:秒、纳秒)
participant_qos.wire_protocol().builtin.discovery_config.discoveryServer_client_syncperiod =
Duration_t(0, 250000000);
XML 配置示例
<?xml version="1.0" encoding="UTF-8" ?>
<profiles xmlns="http://www.eprosima.com">
<participant profile_name="participant_profile_ping" >
<rtps>
<builtin>
<discovery_config>
<!-- 客户端通告周期(将默认450ms改为250ms) -->
<clientAnnouncementPeriod>
<nanosec>250000000</nanosec>
</clientAnnouncementPeriod>
</discovery_config>
</builtin>
</rtps>
</participant>
</profiles>
5.3.4.5 作为可选服务器唯一标识符的 GuidPrefix(The GuidPrefix as an optional server unique identifier)
GuidPrefix_t是 RTPS 规范中的属性,用于唯一标识每个 RTPS 参与者(RTPSParticipant),由 12 字节组成。在 Fast DDS 中,它是 DDS 域内域参与者的标识键(Key),并作为WireProtocolConfigQos类的公有数据成员存在。
在新版发现服务器机制中,GuidPrefix_t是完全可选的参数;但在某些场景下(如与 Fast DDS v2.x 或更早版本的发现服务器实体交互时),由于旧版本中GuidPrefix_t为必填项,因此需手动配置。
5.3.4.5.1 服务器端配置(Server side setup)
以下示例展示如何通过代码(两种方式)和 XML 配置GuidPrefix_t:
C++ 代码示例(方式 1:使用流运算符)
// 使用“>>”运算符和std::istringstream类型解析GuidPrefix
DomainParticipantQos serverQos;
std::istringstream("44.53.00.5f.45.50.52.4f.53.49.4d.41") >>
serverQos.wire_protocol().prefix;
C++ 代码示例(方式 2:手动设置 ASCII 格式的无符号字符)
// 手动定义GuidPrefix的12字节ASCII值
eprosima::fastdds::rtps::GuidPrefix_t serverGuidPrefix;
serverGuidPrefix.value[0] = eprosima::fastdds::rtps::octet(0x44);
serverGuidPrefix.value[1] = eprosima::fastdds::rtps::octet(0x53);
serverGuidPrefix.value[2] = eprosima::fastdds::rtps::octet(0x00);
serverGuidPrefix.value[3] = eprosima::fastdds::rtps::octet(0x5f);
serverGuidPrefix.value[4] = eprosima::fastdds::rtps::octet(0x45);
serverGuidPrefix.value[5] = eprosima::fastdds::rtps::octet(0x50);
serverGuidPrefix.value[6] = eprosima::fastdds::rtps::octet(0x52);
serverGuidPrefix.value[7] = eprosima::fastdds::rtps::octet(0x4f);
serverGuidPrefix.value[8] = eprosima::fastdds::rtps::octet(0x53);
serverGuidPrefix.value[9] = eprosima::fastdds::rtps::octet(0x49);
serverGuidPrefix.value[10] = eprosima::fastdds::rtps::octet(0x4d);
serverGuidPrefix.value[11] = eprosima::fastdds::rtps::octet(0x41);
// 将自定义GuidPrefix赋值给服务器QoS
DomainParticipantQos serverQos;
serverQos.wire_protocol().prefix = serverGuidPrefix;
XML 配置示例
<?xml version="1.0" encoding="UTF-8" ?>
<profiles xmlns="http://www.eprosima.com">
<participant profile_name="participant_server_guidprefix" >
<rtps>
<!-- GuidPrefix配置 -->
<prefix>44.53.00.5f.45.50.52.4f.53.49.4d.41</prefix>
</rtps>
</participant>
</profiles>
重要提示为服务器选择 GuidPrefix 时,需注意:Fast DDS 还通过该参数识别同一主机 / 进程内的参与者、将定位器转换为本地主机地址(localhost),或启用进程内通信(Intra-process Communications)。建议由 Fast DDS 自动生成 GuidPrefix,以确保上述功能正常工作。若将两个域参与者的 GuidPrefix 设为 “进程内兼容”,但实际运行在不同进程中,会导致通信失败。更多信息请参考《进程内传输的 GuidPrefix 注意事项》(GUID Prefix considerations for intra-process delivery)。
警告启动多个使用相同 GuidPrefix 的服务器,会导致未定义行为(Undefined Behavior)。
5.3.4.6 运行时修改远程服务器列表(Modifying remote servers list at run time)
服务器或客户端运行期间,可通过代码修改其待连接的远程服务器列表。具体操作:创建修改后的WireProtocolConfigQos(详见WireProtocolConfigQos文档),并调用DomainParticipant::set_qos()方法应用新配置。
该功能支持:向发现服务器网络中添加新远程服务器,或在远程服务器以新监听定位器重启时,更新其定位器信息。
重要提示更新后的远程服务器列表仅会修改客户端 / 服务器的 ping 流程,不影响已建立的连接。因此,从列表中删除某个定位器不会断开与对应远程服务器的连接,但会导致连接丢失后无法重连。
注意也可通过
ROS_DISCOVERY_SERVER环境变量修改远程服务器列表,详情请参考《FASTDDS_ENVIRONMENT_FILE》文档。
警告强烈建议仅使用 API 或环境文件中的一种方式修改列表,同时使用两种方式可能导致未定义行为。
C++ 代码示例
// 获取服务器/客户端当前的QoS配置
DomainParticipantQos client_or_server_qos;
client_or_server->get_qos(client_or_server_qos);
/* 创建待添加的新服务器条目 */
// 设置新服务器的PDP监听定位器
Locator_t locator;
IPLocator::setIPv4(locator, 127, 0, 0, 1); // 本地地址(127.0.0.1)
locator.port = 11812; // 服务器端口
/* 更新客户端/服务器的远程服务器列表 */
client_or_server_qos.wire_protocol().builtin.discovery_config.m_DiscoveryServers.push_back(locator);
// 应用新QoS配置,若失败则返回错误
if (RETCODE_OK != client_or_server->set_qos(client_or_server_qos))
{
// 错误处理逻辑
return;
}
5.3.4.7 使用名称配置发现服务器定位器(Configure Discovery Server locators using names)
前文发现服务器设置的示例均使用 IPv4 地址指定服务器监听定位器。此外,Fast DDS 也支持通过名称(Names) 配置定位器地址。
5.3.4.8 完整示例(Full example)
以下示例展示如何通过代码和 XML 完整配置服务器与客户端。也可参考 eProsima Fast DDS 的 GitHub 仓库,其中包含与本节示例类似的代码,以及适用于不同场景的其他示例。
5.3.4.8.1 服务器端配置(Server side setup)
C++ 代码示例
// 获取默认参与者QoS
DomainParticipantQos server_qos = PARTICIPANT_QOS_DEFAULT;
// 将参与者角色设为SERVER
server_qos.wire_protocol().builtin.discovery_config.discoveryProtocol =
DiscoveryProtocol::SERVER;
// 设置SERVER的PDP监听定位器
Locator_t locator;
IPLocator::setIPv4(locator, 127, 0, 0, 1); // 本地地址(127.0.0.1)
locator.port = 11811; // 服务器端口
// 将定位器添加到元流量单播定位器列表
server_qos.wire_protocol().builtin.metatrafficUnicastLocatorList.push_back(locator);
/* 添加当前服务器待连接的远程服务器 */
// 设置远程SERVER的PDP监听定位器
Locator_t remote_locator;
IPLocator::setIPv4(remote_locator, 127, 0, 0, 1); // 远程服务器本地地址
remote_locator.port = 11812; // 远程服务器端口
// 将远程服务器添加到当前服务器的服务器列表
server_qos.wire_protocol().builtin.discovery_config.m_DiscoveryServers.push_back(remote_locator);
// 创建SERVER类型的域参与者
DomainParticipant* server =
DomainParticipantFactory::get_instance()->create_participant(0, server_qos);
if (nullptr == server)
{
// 错误处理逻辑
return;
}
XML 配置示例
<?xml version="1.0" encoding="UTF-8" ?>
<profiles xmlns="http://www.eprosima.com">
<participant profile_name="participant_profile_server_full_example">
<rtps>
<builtin>
<discovery_config>
<!-- 将参与者角色设为SERVER -->
<discoveryProtocol>SERVER</discoveryProtocol>
<!-- 当前服务器待连接的远程服务器列表(定位器) -->
<discoveryServersList>
<!-- 远程SERVER的PDP监听定位器 -->
<locator>
<udpv4>
<address>127.0.0.1</address>
<port>11812</port>
</udpv4>
</locator>
</discoveryServersList>
</discovery_config>
<!-- 当前SERVER自身的PDP监听定位器 -->
<metatrafficUnicastLocatorList>
<locator>
<udpv4>
<address>127.0.0.1</address>
<port>11811</port>
</udpv4>
</locator>
</metatrafficUnicastLocatorList>
</builtin>
</rtps>
</participant>
</profiles>
5.3.4.8.2 客户端配置(Client side setup)
C++ 代码示例
// 获取默认参与者QoS
DomainParticipantQos client_qos = PARTICIPANT_QOS_DEFAULT;
// 将参与者角色设为CLIENT
client_qos.wire_protocol().builtin.discovery_config.discoveryProtocol =
DiscoveryProtocol::CLIENT;
// 设置待连接SERVER的PDP监听定位器
Locator_t locator;
IPLocator::setIPv4(locator, 127, 0, 0, 1); // 服务器本地地址(127.0.0.1)
locator.port = 11811; // 服务器端口
// 将服务器添加到客户端的服务器列表
client_qos.wire_protocol().builtin.discovery_config.m_DiscoveryServers.push_back(locator);
// 设置ping周期为250毫秒
client_qos.wire_protocol().builtin.discovery_config.discoveryServer_client_syncperiod =
Duration_t(0, 250000000);
// 创建CLIENT类型的域参与者
DomainParticipant* client =
DomainParticipantFactory::get_instance()->create_participant(0, client_qos);
if (nullptr == client)
{
// 错误处理逻辑
return;
}
XML 配置示例
<?xml version="1.0" encoding="UTF-8" ?>
<profiles xmlns="http://www.eprosima.com">
<participant profile_name="participant_profile_client_full_example">
<rtps>
<builtin>
<discovery_config>
<!-- 将参与者角色设为CLIENT -->
<discoveryProtocol>CLIENT</discoveryProtocol>
<!-- 客户端待连接的远程服务器列表(定位器) -->
<discoveryServersList>
<!-- 远程SERVER的PDP监听定位器 -->
<locator>
<udpv4>
<address>127.0.0.1</address>
<port>11811</port>
</udpv4>
</locator>
</discoveryServersList>
<!-- 设置ping周期为250毫秒 -->
<clientAnnouncementPeriod>
<nanosec>250000000</nanosec>
</clientAnnouncementPeriod>
</discovery_config>
</builtin>
</rtps>
</participant>
</profiles>
5.3.4.9 安全性(Security)
服务器与客户端的安全性配置,与其他类型的参与者配置方式一致。本节主要说明:安全性配置对客户端 - 服务器通信的限制,以及服务器根据所连接客户端 / 服务器的安全配置,决定如何传播发现信息。
核心配置要求
- 编译支持:启用发现服务器的安全发现功能前,Fast DDS 必须编译安全支持(详见《CMake 选项》(CMake options)文档)。
- 域治理文档:域治理文档(Domain Governance Document)必须明确对发现过程进行加密。
关键规则
- 治理文档一致性:与简单发现(SDP)类似,连接到同一服务器的所有客户端和服务器,其域治理文档必须与该服务器的域治理文档一致 —— 即同一发现服务器网络中的所有域参与者,必须以相同方式配置发现保护(Discovery Protection)。
- 客户端安全通信:尽管服务器会介导发现过程并建立客户端间连接,但客户端之间仍需通过公钥基础设施(PKI,Public Key Infrastructure)交换,以实现安全通信。
重要提示
- 为保持与 QoS 策略的行为一致性,服务器不检查待连接域参与者的域参与者权限文档(DomainParticipant Permissions Document)。
- 发现服务器的安全支持仅从 Fast DDS v2.10.0 版本开始提供。
5.3.5 域参与者监听器发现回调(DomainParticipantListener Discovery Callbacks)
正如《域参与者监听器》(DomainParticipantListener)部分所述,DomainParticipantListener是一个抽象类,用于定义响应域参与者(DomainParticipant)状态变化的回调函数。Fast DDS 针对发现过程中可能发生的事件,定义了三个回调函数:on_participant_discovery()(参与者发现回调)、on_data_reader_discovery()(数据读取器发现回调)、on_data_writer_discovery()(数据写入器发现回调)。关于DomainParticipantListener的更多信息,可参考《域参与者监听器》(DomainParticipantListener)章节。
以下是域参与者监听器发现回调的实现示例:
class DiscoveryDomainParticipantListener : public DomainParticipantListener
{
/* 自定义参与者发现回调:on_participant_discovery */
void on_participant_discovery(
DomainParticipant* participant,
eprosima::fastdds::rtps::ParticipantDiscoveryStatus status,
const ParticipantBuiltinTopicData& info,
bool& should_be_ignored) override
{
should_be_ignored = false; // 默认不忽略发现的参与者
static_cast<void>(participant); // 避免未使用参数的编译警告
// 根据参与者发现状态执行不同逻辑
switch (status)
{
case eprosima::fastdds::rtps::ParticipantDiscoveryStatus::DISCOVERED_PARTICIPANT:
{
/* 处理“在域中发现新域参与者”的场景 */
std::cout << "发现新域参与者:名称为 '" << info.participant_name
<< "',ID为 '" << info.guid.entityId
<< "',GuidPrefix为 '" << info.guid.guidPrefix
<< "'。" << std::endl;
/* 以下代码可替换为自定义逻辑,判断是否需要忽略该发现的参与者 */
bool ignoring_condition = false; // 忽略条件(示例中默认不忽略)
if (ignoring_condition)
{
should_be_ignored = true; // 请求忽略该发现的参与者
}
}
break;
case eprosima::fastdds::rtps::ParticipantDiscoveryStatus::CHANGED_QOS_PARTICIPANT:
/* 处理“域参与者修改了QoS(服务质量)”的场景 */
break;
case eprosima::fastdds::rtps::ParticipantDiscoveryStatus::REMOVED_PARTICIPANT:
/* 处理“域参与者从域中移除”的场景 */
std::cout << "域参与者离开:名称为 '" << info.participant_name
<< "',ID为 '" << info.guid.entityId
<< "',GuidPrefix为 '" << info.guid.guidPrefix
<< "'。" << std::endl;
break;
}
}
/* 自定义数据读取器发现回调:on_data_reader_discovery */
void on_data_reader_discovery(
DomainParticipant* participant,
eprosima::fastdds::rtps::ReaderDiscoveryStatus reason,
const eprosima::fastdds::rtps::SubscriptionBuiltinTopicData& info,
bool& should_be_ignored) override
{
should_be_ignored = false; // 默认不忽略发现的数据读取器
static_cast<void>(participant); // 避免未使用参数的编译警告
// 根据数据读取器发现原因执行不同逻辑
switch (reason)
{
case eprosima::fastdds::rtps::ReaderDiscoveryStatus::DISCOVERED_READER:
{
/* 处理“在域中发现新数据读取器”的场景 */
std::cout << "发现新数据读取器:订阅的主题为 '" << info.topic_name
<< "',数据类型为 '" << info.type_name
<< "'。" << std::endl;
/* 以下代码可替换为自定义逻辑,判断是否需要忽略该发现的数据读取器 */
bool ignoring_condition = false; // 忽略条件(示例中默认不忽略)
if (ignoring_condition)
{
should_be_ignored = true; // 请求忽略该发现的数据读取器
}
}
break;
case eprosima::fastdds::rtps::ReaderDiscoveryStatus::CHANGED_QOS_READER:
/* 处理“数据读取器修改了QoS”的场景 */
break;
case eprosima::fastdds::rtps::ReaderDiscoveryStatus::REMOVED_READER:
/* 处理“数据读取器从域中移除”的场景 */
std::cout << "数据读取器离开:订阅的主题为 '" << info.topic_name
<< "',数据类型为 '" << info.type_name
<< "'。" << std::endl;
break;
}
}
/* 自定义数据写入器发现回调:on_data_writer_discovery */
void on_data_writer_discovery(
DomainParticipant* participant,
eprosima::fastdds::rtps::WriterDiscoveryStatus reason,
const eprosima::fastdds::dds::PublicationBuiltinTopicData& info,
bool& should_be_ignored) override
{
should_be_ignored = false; // 默认不忽略发现的数据写入器
static_cast<void>(participant); // 避免未使用参数的编译警告
// 根据数据写入器发现原因执行不同逻辑
switch (reason)
{
case eprosima::fastdds::rtps::WriterDiscoveryStatus::DISCOVERED_WRITER:
{
/* 处理“在域中发现新数据写入器”的场景 */
std::cout << "发现新数据写入器:发布的主题为 '" << info.topic_name
<< "',数据类型为 '" << info.type_name
<< "'。" << std::endl;
/* 以下代码可替换为自定义逻辑,判断是否需要忽略该发现的数据写入器 */
bool ignoring_condition = false; // 忽略条件(示例中默认不忽略)
if (ignoring_condition)
{
should_be_ignored = true; // 请求忽略该发现的数据写入器
}
}
break;
case eprosima::fastdds::rtps::WriterDiscoveryStatus::CHANGED_QOS_WRITER:
/* 处理“数据写入器修改了QoS”的场景 */
break;
case eprosima::fastdds::rtps::WriterDiscoveryStatus::REMOVED_WRITER:
/* 处理“数据写入器从域中移除”的场景 */
std::cout << "数据写入器离开:发布的主题为 '" << info.topic_name
<< "',数据类型为 '" << info.type_name
<< "'。" << std::endl;
break;
}
}
};
发现回调的使用方法
上述示例中,DiscoveryDomainParticipantListener类继承自DomainParticipantListener并实现了自定义发现回调。要使用这些回调,需创建该类的对象,并将其注册为域参与者的监听器,具体步骤如下:
// 1. 创建域参与者QoS(服务质量)对象并配置参数
DomainParticipantQos pqos;
// 2. 创建自定义域参与者监听器对象
DiscoveryDomainParticipantListener* plistener = new DiscoveryDomainParticipantListener();
// 3. 创建域参与者时,将自定义监听器传入(注册监听器)
DomainParticipant* participant =
DomainParticipantFactory::get_instance()->create_participant(
0, // 域ID(此处为0,即默认域)
pqos, // 域参与者QoS配置
plistener // 已创建的自定义监听器
);
1108

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



