1. Hello World 发布订阅

Hello World 发布订阅 学习卡片

任务目标

使用FastDDS创建一个发布者节点和一个订阅者节点,实现字符串消息“Hello World”的单向传输。发布者周期性发送消息,订阅者接收并打印消息内容。

环境依赖

需要安装FastDDS库及其依赖(如Foonathan memory、ASIO等)。推荐使用FastDDS 2.x版本,并确保编译环境支持C++11及以上标准。若使用CMake构建,需在系统中找到FastDDS的安装路径。

核心实现

以下代码包含完整的IDL定义、发布者与订阅者实现。IDL文件用于生成数据类型,发布者创建Publisher并周期发送消息,订阅者创建Subscriber并注册回调函数接收消息。

// HelloWorld.idl
struct HelloWorld
{
    string msg;
};
// publisher.cpp
#include <iostream>
#include <thread>
#include <chrono>
#include <fastdds/dds/domain/DomainParticipant.hpp>
#include <fastdds/dds/domain/DomainParticipantFactory.hpp>
#include <fastdds/dds/publisher/Publisher.hpp>
#include <fastdds/dds/publisher/DataWriter.hpp>
#include <fastdds/dds/publisher/DataWriterListener.hpp>
#include <fastdds/dds/topic/Topic.hpp>
#include <fastdds/dds/topic/TypeSupport.hpp>
#include "HelloWorldPubSubTypes.h"

using namespace eprosima::fastdds::dds;

class PubListener : public DataWriterListener
{
public:
    PubListener() : matched(0) {}
    void on_publication_matched(DataWriter*, const PublicationMatchedStatus& info) override
    {
        if (info.current_count_change == 1) matched = info.current_count;
        else if (info.current_count_change == -1) matched = info.current_count;
    }
    int matched;
};

int main()
{
    DomainParticipantQos pqos = PARTICIPANT_QOS_DEFAULT;
    DomainParticipant* participant = DomainParticipantFactory::get_instance().create_participant(0, pqos);
    if (!participant) return 1;

    HelloWorldPubSubType type;
    TypeSupport ts(&type);
    participant->register_type(ts);

    Topic* topic = participant->create_topic("HelloWorldTopic", "HelloWorld", TOPIC_QOS_DEFAULT);
    if (!topic) return 1;

    Publisher* publisher = participant->create_publisher(PUBLISHER_QOS_DEFAULT);
    if (!publisher) return 1;

    PubListener listener;
    DataWriterQos wqos = DATAWRITER_QOS_DEFAULT;
    DataWriter* writer = publisher->create_datawriter(topic, wqos, &listener);
    if (!writer) return 1;

    HelloWorld sample;
    sample.msg("Hello World");
    int count = 0;
    while (count < 10)
    {
        if (listener.matched > 0)
        {
            writer->write(&sample);
            std::cout << "Sent: " << sample.msg() << std::endl;
            count++;
        }
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }

    participant->delete_contained_entities();
    DomainParticipantFactory::get_instance().delete_participant(participant);
    return 0;
}
// subscriber.cpp
#include <iostream>
#include <fastdds/dds/domain/DomainParticipant.hpp>
#include <fastdds/dds/domain/DomainParticipantFactory.hpp>
#include <fastdds/dds/subscriber/Subscriber.hpp>
#include <fastdds/dds/subscriber/DataReader.hpp>
#include <fastdds/dds/subscriber/DataReaderListener.hpp>
#include <fastdds/dds/topic/Topic.hpp>
#include <fastdds/dds/topic/TypeSupport.hpp>
#include "HelloWorldPubSubTypes.h"

using namespace eprosima::fastdds::dds;

class SubListener : public DataReaderListener
{
public:
    void on_data_available(DataReader* reader) override
    {
        HelloWorld sample;
        SampleInfo info;
        if (reader->take_next_sample(&sample, &info) == ReturnCode_t::RETCODE_OK)
        {
            if (info.valid_data)
                std::cout << "Received: " << sample.msg() << std::endl;
        }
    }
};

int main()
{
    DomainParticipant* participant = DomainParticipantFactory::get_instance().create_participant(0, PARTICIPANT_QOS_DEFAULT);
    if (!participant) return 1;

    HelloWorldPubSubType type;
    TypeSupport ts(&type);
    participant->register_type(ts);

    Topic* topic = participant->create_topic("HelloWorldTopic", "HelloWorld", TOPIC_QOS_DEFAULT);
    if (!topic) return 1;

    Subscriber* subscriber = participant->create_subscriber(SUBSCRIBER_QOS_DEFAULT);
    if (!subscriber) return 1;

    SubListener listener;
    DataReaderQos rqos = DATAREADER_QOS_DEFAULT;
    DataReader* reader = subscriber->create_datareader(topic, rqos, &listener);
    if (!reader) return 1;

    std::this_thread::sleep_for(std::chrono::seconds(15));
    participant->delete_contained_entities();
    DomainParticipantFactory::get_instance().delete_participant(participant);
    return 0;
}

编译与运行

使用FastDDS提供的fastddsgen工具从IDL生成类型支持代码,然后编译两个可执行文件:

fastddsgen HelloWorld.idl
g++ -std=c++11 publisher.cpp HelloWorldPubSubTypes.cxx HelloWorld.cxx -o publisher -lfastdds -lfastcdr
g++ -std=c++11 subscriber.cpp HelloWorldPubSubTypes.cxx HelloWorld.cxx -o subscriber -lfastdds -lfastcdr

先在一个终端运行订阅者,再在另一个终端运行发布者:

./subscriber
./publisher

验证方法

观察订阅者终端是否打印出10条“Received: Hello World”消息。若发布者终端显示“Sent: Hello World”且订阅者同步收到相同内容,则说明发布订阅通信成功。若订阅者无输出,检查两端是否使用相同的Domain ID(本例均为0)和Topic名称(HelloWorldTopic)。