5. 发现(Discovery)

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)” 部分)DiscoveryProtocolSIMPLE
忽略参与者标志(Ignore Participant flags)对来自同一进程、不同进程或不同主机的域参与者(DomainParticipant)的发现流量进行过滤ParticipantFilteringFlagsNO_FILTER
租约时长(Lease Duration)远程域参与者将本地域参与者视为 “存活” 状态的时长Duration_t20 秒
通告周期(Announcement Period)域参与者发送 PDP(参与者发现阶段)通告的周期Duration_t3 秒

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 层的addReaderLocatoraddReaderProxyaddWriterProxy方法手动完成
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_t5
period(周期)定义初始通告的特定发送周期Duration_t100 毫秒
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、两者皆创建或两者皆不创建booltrue
Publication writer and Subscription reader(发布写入器与订阅读取器)适用于仅实现一个或多个 DataWriter(即不实现 DataReader)的域参与者,仅允许创建与 DataReader 发现相关的 EDP 端点booltrue
Publication reader and Subscription writer(发布读取器与订阅写入器)适用于仅实现一个或多个 DataReader(即不实现 DataWriter)的域参与者,仅允许创建与 DataWriter 发现相关的 EDP 端点booltrue
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/DataWriteruint16_t0
<entityID>DataReader/DataWriter 的实体 IDuint16_t0
<expects_inline_qos>指示是否期望 QoS(服务质量)内联(仅适用于 DataReader)boolfalse
<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)

要理解该架构,需掌握以下核心概念:

  1. 协议与实体复用:发现服务器机制复用 RTPS 发现消息结构,以及标准 DDS 的 DataWriter(数据写入器)和 DataReader(数据读取器)。
  2. 角色划分:发现服务器的域参与者分为客户端(Client)和服务器(Server)两类。二者的唯一区别是处理发现流量的方式不同;而它们创建的 DataWriter 与 DataReader 之间的用户流量(User Traffic),与角色无关。
  3. 服务器(SERVER)
    • 定义:客户端(或其他服务器)向其发送发现信息的参与者。
    • 核心作用:将客户端的发现信息分发给已知的客户端和服务器;同时,服务器会向已知服务器通告新服务器的存在(反之亦然)—— 因此,新服务器只需知晓网络中任意一个现有服务器,即可与所有其他现有服务器建立连接。
    • 信息来源:分发的发现信息既可能来自直接连接的客户端,也可能来自其他服务器转发的其客户端信息。
    • 信息分发规则:
      • 已知服务器:接收该服务器直接客户端的所有信息,以及其他服务器的参与者信息(用于通告新服务器)。
      • 已知客户端:仅接收建立通信所需的信息(即与客户端匹配的域参与者、DataWriter 和 DataReader 信息)—— 这意味着服务器会运行 “匹配算法”,筛选出每个客户端所需的信息。
  4. 备份服务器(BACKUP)
    • 定义:将发现数据库持久化到文件中的服务器。
    • 特性:启动时可从文件加载网络拓扑图,无需接收任何客户端信息;能在服务器运行期间持久化网络认知,避免意外关闭导致信息丢失。
    • 注意事项:由于需定期向文件写入数据(开销较大),使用备份服务器会对发现速度产生负面影响。
  5. 客户端(CLIENT)
    • 定义:连接到一个或多个服务器,仅从服务器接收与匹配端点建立通信所需发现信息的参与者。
    • 前置要求:需提前知晓待连接服务器的定位器列表(包含服务器监听的 IP 地址、端口,以及客户端与服务器通信的传输协议 ——UDP 或 TCP)。
  6. 超级客户端(SUPER_CLIENT)
    • 定义:接收服务器已知的所有发现信息的客户端(区别于仅接收所需信息的普通客户端)。
    • 注意事项:超级客户端不具备服务器功能 —— 仅通过连接的服务器接收信息,不连接其他服务器,也不转发接收的信息;且服务器发现的无端点域参与者,超级客户端无法知晓。
  7. 通信逻辑
    • 服务器无需提前知晓客户端,但必须在客户端已知的定位器指定地址上监听。
    • 客户端会定期(按 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)

服务器与客户端的安全性配置,与其他类型的参与者配置方式一致。本节主要说明:安全性配置对客户端 - 服务器通信的限制,以及服务器根据所连接客户端 / 服务器的安全配置,决定如何传播发现信息。

核心配置要求

  1. 编译支持:启用发现服务器的安全发现功能前,Fast DDS 必须编译安全支持(详见《CMake 选项》(CMake options)文档)。
  2. 域治理文档:域治理文档(Domain Governance Document)必须明确对发现过程进行加密。

关键规则

  1. 治理文档一致性:与简单发现(SDP)类似,连接到同一服务器的所有客户端和服务器,其域治理文档必须与该服务器的域治理文档一致 —— 即同一发现服务器网络中的所有域参与者,必须以相同方式配置发现保护(Discovery Protection)。
  2. 客户端安全通信:尽管服务器会介导发现过程并建立客户端间连接,但客户端之间仍需通过公钥基础设施(PKI,Public Key Infrastructure)交换,以实现安全通信。

重要提示

  1. 为保持与 QoS 策略的行为一致性,服务器不检查待连接域参与者的域参与者权限文档(DomainParticipant Permissions Document)。
  2. 发现服务器的安全支持仅从 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           // 已创建的自定义监听器
);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值