Fast DDS 默认传输机制详解:共享内存与 UDP 的智能选择

Fast DDS 默认传输机制详解:共享内存与 UDP 的智能选择

🧩 一、Fast DDS 默认传输层机制概览

Fast DDS 的通信底层是由 RTPS(Real-Time Publish-Subscribe)协议驱动的,而 RTPS 的数据实际传输由 Transport 层 决定。

Fast DDS 支持多种传输方式(Transport Layer Plugins):

传输层类型 类名 说明
共享内存传输(SHM) SharedMemTransportDescriptor 用于同一主机不同进程间通信(零拷贝)
UDPv4 传输 UDPv4TransportDescriptor 用于跨主机网络通信
UDPv6 传输 UDPv6TransportDescriptor IPv6 网络通信
TCP 传输(可选) TCPv4TransportDescriptor 可选插件,需手动启用
内存内传输(Intraprocess) 内置 同一进程内通信

⚙️ 二、默认配置

你当前的代码中:

cpp 复制代码
DomainParticipantQos participant_qos = PARTICIPANT_QOS_DEFAULT;
participant_ = DomainParticipantFactory::get_instance()->create_participant(0, participant_qos);

这就意味着使用了 Fast DDS 默认 QoS + 默认 Transport 配置

根据官方文档(2.3 之后的版本):

✅ 默认启用 UDPv4Shared Memory (SHM) 传输层,且共享内存优先用于 同主机进程间通信,UDP 仅在检测到远程主机时使用。

🔍 三、默认传输选择逻辑(自动判断)

当 Publisher 和 Subscriber 匹配成功后,Fast DDS 内部会自动判断:

场景 实际使用的传输层 说明
同一进程 Intraprocess 通信(内存直传) 完全无网络、无拷贝
同一主机(不同进程) Shared Memory (SHM) 使用共享内存环形缓冲区
不同主机 UDPv4 标准网络通信

💡 优先级顺序:Intraprocess > Shared Memory > UDP

🛠️ 四、自定义 QoS 配置传输层

1. 完全自定义传输配置

cpp 复制代码
#include <fastdds/rtps/transport/UDPv4TransportDescriptor.h>
#include <fastdds/rtps/transport/SharedMemTransportDescriptor.h>
#include <fastdds/rtps/transport/TCPv4TransportDescriptor.h>

// 创建自定义 QoS
DomainParticipantQos participant_qos;

// 禁用内置传输,使用自定义配置
participant_qos.transport().use_builtin_transports = false;

// 方案1:只使用 UDP(禁用共享内存)
auto udp_transport = std::make_shared<eprosima::fastdds::rtps::UDPv4TransportDescriptor>();
udp_transport->sendBufferSize = 65536;  // 自定义缓冲区大小
udp_transport->receiveBufferSize = 65536;
participant_qos.transport().user_transports.push_back(udp_transport);

// 方案2:只使用共享内存(禁用网络)
auto shm_transport = std::make_shared<eprosima::fastdds::rtps::SharedMemTransportDescriptor>();
shm_transport->segment_size(16 * 1024 * 1024);  // 16MB 共享内存段
participant_qos.transport().user_transports.push_back(shm_transport);

// 方案3:同时使用 UDP 和共享内存,但调整优先级
participant_qos.transport().user_transports.push_back(shm_transport);
participant_qos.transport().user_transports.push_back(udp_transport);

// 创建参与者
participant_ = factory->create_participant(0, participant_qos);

2. 基于内置传输的微调

cpp 复制代码
// 使用内置传输,但调整参数
DomainParticipantQos participant_qos = PARTICIPANT_QOS_DEFAULT;

// 获取内置 UDP 传输描述符并调整
auto udp_transport = 
    std::static_pointer_cast<eprosima::fastdds::rtps::UDPv4TransportDescriptor>(
        participant_qos.transport().user_transports.front());
        
if (udp_transport) {
    udp_transport->maxMessageSize = 65536;    // 最大消息大小
    udp_transport->sendBufferSize = 131072;   // 发送缓冲区
    udp_transport->receiveBufferSize = 131072; // 接收缓冲区
}

3. 发布者/订阅者级别的传输配置

cpp 复制代码
// 发布者 QoS 配置
DataWriterQos writer_qos = DATAWRITER_QOS_DEFAULT;
writer_qos.publish_mode().kind = eprosima::fastdds::dds::ASYNCHRONOUS_PUBLISH_MODE;
writer_qos.reliability().kind = eprosima::fastdds::dds::RELIABLE_RELIABILITY_QOS;

// 订阅者 QoS 配置  
DataReaderQos reader_qos = DATAREADER_QOS_DEFAULT;
reader_qos.reliability().kind = eprosima::fastdds::dds::RELIABLE_RELIABILITY_QOS;
reader_qos.history().kind = eprosima::fastdds::dds::KEEP_LAST_HISTORY_QOS;
reader_qos.history().depth = 50;

📄 五、传输层验证与监控

1. 环境变量启用详细日志

bash 复制代码
# Windows
set FASTDDS_LOG_VERBOSITY=info
set FASTDDS_LOG_FILTER=RTPS_TRANSPORT

# Linux
export FASTDDS_LOG_VERBOSITY=info
export FASTDDS_LOG_FILTER=RTPS_TRANSPORT

2. 编程方式启用传输日志

cpp 复制代码
#include <fastdds/dds/log/Log.hpp>

// 在程序开始时设置日志
eprosima::fastdds::dds::Log::SetVerbosity(eprosima::fastdds::dds::Log::Info);
eprosima::fastdds::dds::Log::SetCategoryFilter(std::regex("RTPS_TRANSPORT"));

3. 完整的传输监控示例代码

cpp 复制代码
#include <fastdds/dds/domain/DomainParticipant.hpp>
#include <fastdds/dds/domain/DomainParticipantListener.hpp>
#include <fastdds/dds/log/Log.hpp>
#include <iostream>

class TransportMonitoringListener : public eprosima::fastdds::dds::DomainParticipantListener 
{
public:
    virtual void on_participant_discovery(
        eprosima::fastdds::dds::DomainParticipant* participant,
        eprosima::fastrtps::rtps::ParticipantDiscoveryInfo&& info) override 
    {
        if (info.status == eprosima::fastrtps::rtps::ParticipantDiscoveryInfo::DISCOVERED_PARTICIPANT) {
            std::cout << "发现新的参与者: " << info.info.m_participantName << std::endl;
            print_transport_info(participant);
        }
    }

    void print_transport_info(eprosima::fastdds::dds::DomainParticipant* participant) 
    {
        auto rtps_participant = participant->get_rtps_participant();
        if (!rtps_participant) return;

        std::cout << "当前传输统计:" << std::endl;
        
        // 获取所有已注册的传输
        auto& transports = rtps_participant->get_network_factory().get_all_transport_descriptors();
        for (const auto& transport : transports) {
            std::cout << "   - 传输类型: " << transport->get_type_name() << std::endl;
        }

        // 获取发送资源限制
        auto send_resource_limits = rtps_participant->get_attributes().allocation.send_ports;
        std::cout << "   - 发送端口限制: " << send_resource_limits.initial << "/" 
                  << send_resource_limits.maximum << std::endl;
    }
};

// 使用监控监听器
void create_monitored_participant() 
{
    auto factory = eprosima::fastdds::dds::DomainParticipantFactory::get_instance();
    eprosima::fastdds::dds::DomainParticipantQos qos = 
        eprosima::fastdds::dds::PARTICIPANT_QOS_DEFAULT;
    
    auto listener = std::make_shared<TransportMonitoringListener>();
    auto participant = factory->create_participant(0, qos, listener.get());
    
    if (participant) {
        std::cout << "参与者创建成功,开始监控传输层..." << std::endl;
    }
}

4. 实时传输类型检测

cpp 复制代码
#include <fastdds/rtps/transport/TransportInterface.h>

class TransportDetector {
public:
    static void detect_current_transport(eprosima::fastdds::dds::DataReader* reader) 
    {
        auto rtps_reader = reader->get_rtps_reader();
        if (!rtps_reader) return;

        // 获取当前匹配的写入器
        eprosima::fastrtps::rtps::GUID_t writer_guid;
        // 这里需要根据实际匹配情况获取 GUID
        
        std::cout << "检测数据传输路径..." << std::endl;
        
        // 通过检查本地定位器来判断传输类型
        auto& att = rtps_reader->getAttributes();
        for (const auto& locator : att.endpoint.unicastLocatorList) {
            std::string transport_type = "未知";
            
            if (locator.kind == LOCATOR_KIND_UDPv4) {
                transport_type = "UDPv4";
            } else if (locator.kind == LOCATOR_KIND_SHM) {
                transport_type = "共享内存(SHM)";
            }
            
            std::cout << "   - 定位器: " << locator << " -> 传输: " << transport_type << std::endl;
        }
    }
};

🧪 六、实际日志输出示例

启用传输日志后,你会看到类似输出:

复制代码
[RTPS_TRANSPORT] SHM Transport registered.
[RTPS_TRANSPORT] UDPv4 Transport registered.
[RTPS_TRANSPORT] Using SHM transport for locator: shm://0.0.0.0
[RTPS_TRANSPORT] Using UDPv4 transport for locator: udp://192.168.1.5
[RTPS_MSG_OUT] Sending message via SHM to participant [0.0.1.c1]
[RTPS_MSG_IN] Receiving message via UDPv4 from 192.168.1.6:7412

日志分析:

  • SHM Transport registered → 共享内存传输已注册
  • UDPv4 Transport registered → UDPv4 传输已注册
  • Using SHM transport → 实际使用共享内存传输(本地通信)
  • Using UDPv4 transport → 实际使用 UDP 传输(跨机通信)

🚫 七、常见问题排查

1. 强制使用特定传输

cpp 复制代码
// 强制使用 UDP,即使在同一主机上
DomainParticipantQos qos = PARTICIPANT_QOS_DEFAULT;
qos.transport().use_builtin_transports = false;

auto udp_transport = std::make_shared<UDPv4TransportDescriptor>();
// 禁用回环,强制走网络
udp_transport->interfaceWhiteList.push_back("192.168.1.0");
qos.transport().user_transports.push_back(udp_transport);

2. 共享内存问题诊断

cpp 复制代码
// 检查共享内存配置
auto shm_transport = std::make_shared<SharedMemTransportDescriptor>();
shm_transport->segment_size(32 * 1024 * 1024);  // 32MB
shm_transport->max_message_size(64 * 1024);     // 64KB 最大消息

// 在 Linux 上检查共享内存段
std::cout << "检查共享内存段..." << std::endl;
system("ipcs -m");

🧩 八、总结

项目 默认行为 自定义配置能力
同进程通信 内部队列(零拷贝) ✅ 可调整缓冲区大小
同主机进程间通信 ✅ 使用共享内存 ✅ 可禁用或参数调优
跨主机通信 ✅ 使用 UDPv4 ✅ 可配置网络参数
传输优先级 SHM > UDP ✅ 可调整传输顺序
监控能力 基础日志 ✅ 编程式详细监控

最佳实践建议:

  1. 生产环境:保持默认配置,让 Fast DDS 自动选择最优传输
  2. 调试环境:启用传输日志验证实际使用的传输层
  3. 特定场景:根据需要自定义传输配置(如容器环境、特定网络需求)
  4. 性能优化:根据数据大小调整共享内存段和网络缓冲区

你的代码使用默认配置就能获得最佳的性能和灵活性,Fast DDS 会自动为你选择最高效的通信方式!

相关推荐
Kevin Wang7272 小时前
欧拉系统服务部署注意事项
网络·windows
min1811234562 小时前
深度伪造内容的检测与溯源技术
大数据·网络·人工智能
汤愈韬2 小时前
NAT策略
网络协议·网络安全·security·huawei
汤愈韬2 小时前
Full Cone Nat
网络·网络协议·网络安全·security·huawei
zbtlink3 小时前
现在还需要带电池的路由器吗?是用来干嘛的?
网络·智能路由器
桌面运维家3 小时前
vDisk配置漂移怎么办?VOI/IDV架构故障快速修复
网络·架构
dalerkd3 小时前
忙里偷闲叙-谈谈最近两年
网络·安全·web安全
汤愈韬4 小时前
NAT ALG (应用层网关)
网络·网络协议·网络安全·security·huawei
运维栈记5 小时前
虚拟化网络的根基-网络命名空间
网络·docker·容器
五仁火烧5 小时前
生产环境中配置了接口3000后,不能启动,改成8080后就可以
linux·网络·安全·vue