在 Fast DDS 中,
std::vector<std::string> interfaceWhiteList
其核心功能是定义本地网络接口的使用权限,即“本机允许 Fast-DDS 通过哪些网卡进行数据包的发送与接收”。
该机制通过一个名为 interfaceWhiteList 的配置项实现,严格限定 Fast-DDS 可用的网络接口。只有被列入白名单的网卡才会被启用,其余所有接口将被完全忽略——既不会用于监听,也不会用于发包。
为何需要接口白名单?(实际应用场景)
现代设备通常配备多个网络接口,例如:以太网、Wi-Fi、回环接口(loopback)、Docker 虚拟网桥(docker0)、VPN 接口等。若未设置白名单,Fast-DDS 默认会尝试使用所有可用网卡,这可能引发以下严重问题:
- 通信异常或安全风险:数据可能经由公司内网、无线网络或加密隧道(如 VPN)错误地广播出去,导致延迟、丢包甚至信息泄露。
- 组播/广播风暴:特别是针对默认使用的组播地址(如 239.255.0.1),若在多个接口上同时发送,极易造成网络拥塞。
- 系统稳定性隐患:在嵌入式系统或车载环境中,通过非预期的物理接口传输关键数据可能导致节点误判或整体通信紊乱。
何时应禁用多播?
尽管多播机制有助于自动发现和高效通信,但在某些场景下必须关闭:
- 网络不支持多播:部分受限网络环境(如某些云平台或隔离 VLAN)无法正确转发多播报文,导致发现失败。
- 安全合规要求:
- 防止未授权访问:多播发现会向局域网广播参与者的元数据,易被恶意节点监听或仿冒,构成潜在攻击面。
- 满足行业规范:金融、医疗等领域常要求通信范围最小化,禁止任何形式的广播行为以符合监管标准。
创建 DomainParticipant 时指定 DomainId 的作用是什么?
DomainId(域 ID)用于标识当前 DomainParticipant 所属的 DDS 通信域。
DDS(Data Distribution Service)是一种分布式实时通信中间件,支持将整个通信网络划分为多个逻辑隔离的“域”。其核心规则如下:
- 只有拥有相同 DomainId 的参与者才能相互发现并交换数据。
- 不同 DomainId 的参与者即使运行在同一台主机甚至同一进程中,也无法感知彼此的存在,相当于处于独立的“虚拟通信空间”。
因此,DomainId 类似于“频道号”或“群组编号”,决定了你的应用能与哪些其他节点通信。
重要限制说明(基于主流 DDS 实现,如 RTI Connext、OpenDDS、Fast-DDS 等):
- 建议取值范围为 0 ~ 200
尽管 DDS 规范(OMG DDS 1.4)允许 DomainId 范围为 0 到 2-1(约 21 亿),但多数商用与开源实现出于对传统组播发现机制的兼容考虑,官方推荐使用 0~200 范围内的值。超出此范围可能导致发现机制失效,或被迫退化为单播模式。 - 创建后不可更改
一旦 DomainParticipant 被创建,其 DomainId 即被锁定,无法动态修改。如需切换域,必须销毁当前 Participant,并以新的 DomainId 重新创建实例。
规范依据:
参见 OMG DDS 1.4 正式版规范中的章节:
- 2.2.3 Domain Module
- DomainParticipant 的属性
DomainId_t(在 RTI 实现中对应DDS_DomainParticipantQos.domain_id)
一句话总结:DomainId 是决定你的 DDS 应用“跟谁通信”的分组标识,一经设定即不可变更,推荐使用 0~200 之间的整数。
RTPS 协议中的 History 缓存机制
在整个 RTPS 协议栈(作为 DDS 的底层有线协议)中,每个 Reader 和 Writer 都维护一个独立的 History(历史缓存)。
每当发送或接收一条数据,在 RTPS 层被称为一个 Change。Fast DDS 内部将 Change 实现为结构体:CacheChange_t。
所有 CacheChange_t 的生命周期管理——包括插入、删除、查询、老化清理以及按序列号排序——均由 History 类统一负责。
| 角色 | 对应的 History 类 | 主要作用 |
|---|---|---|
| DataWriter | WriterHistory | 保存“已发出的数据”,用于重传及补发历史数据 |
| DataReader | ReaderHistory | 保存“已接收到的数据”,供应用程序 read/take 操作读取 |
在创建 RTPS Writer(即 DataWriter)时,除了配置 WriterAttributes(控制可靠性、流量控制、心跳间隔等行为)外,还必须为其分配一个 WriterHistory 实例。
WriterHistory 的本质是一个本地缓存池,其职责是存储该 Writer 最近发送或尚未完成传输的数据样本(Sample)。
为何必须存在这个缓存?三大关键功能依赖于此:
| 功能 | 缺失 History 的后果 |
|---|---|
| 可靠传输(Reliable) | 无法实现丢失数据的重传,一旦网络波动即导致数据永久丢失 |
1. 当对方未返回 ACK 确认时,必须进行重发操作,而重发所需的数据则来源于 History。因此,History 成为了消息重传的关键数据支撑。
std::vector<std::string> interfaceWhiteList
2. 对于晚加入的 Reader(即 Late Joiner),其需要获取此前已发布的数据内容。这些历史数据统一由 Writer 的 History 提供,确保新成员能够完整接收到错过的数据。
3. 在涉及耐久性(Durability)的场景中,无论是 TRANSIENT 还是 TRANSIENT_LOCAL 模式,都要求即使 Writer 发生重启,历史数据依然得以保留。这一能力依赖于 History 机制,若需实现真正的磁盘持久化,则还需结合 Persistence Service 共同完成。
核心总结: WriterHistory 相当于 Writer 的“记忆”功能,决定了它能保存多少最近发送的数据记录,支持对新加入 Reader 的历史数据补发,并能在通信异常时重新传输丢失的数据包。


雷达卡


京公网安备 11010802022788号







