FastDDS 源码解析(二十三)创建User的DataReader(上)

第一篇 一个例子

车载消息中间件FastDDS 源码解析(一)FastDDS 介绍和使用

第二篇fastdds的组成

车载消息中间件FastDDS 源码解析(二)RtpsParticipant的创建(上)

车载消息中间件FastDDS 源码解析(三)RtpsParticipant的创建(中)

车载消息中间件FastDDS 源码解析(四)RtpsParticipant的创建(下)

车载消息中间件FastDDS 源码解析(五)BuiltinProtocols(上)

车载消息中间件FastDDS 源码解析(六)BuiltinProtocols(中)EDP

车载消息中间件FastDDS 源码解析(七)BuiltinProtocols(下)WLP&TypeLookupManager

车载消息中间件FastDDS 源码解析(八)TimedEvent

车载消息中间件FastDDS 源码解析(九)Message

第三篇组网建立连接

pdp建连

车载消息中间件FastDDS 源码解析(十)发送第一条PDP消息(上)

FastDDS 源码解析(十一)发送第一条PDP消息(中)

FastDDS 源码解析(十二)发送第一条PDP消息(下)---异步发送

FastDDS 源码解析(十三)发送第一条PDP消息---跨进程发送

FastDDS 源码解析(十四)接收PDP消息(上)

FastDDS 源码解析(十五)接收PDP消息(下)

FastDDS 源码解析(十六)处理PDP消息------PDP匹配

EDP建连

FastDDS 源码解析(十七)处理PDP消息------EDP匹配

FastDDS 源码解析(十八)EDP阶段发送心跳heartbeat

FastDDS 源码解析(十九)EDP阶段处理heartbeat消息,发送acknack消息

FastDDS 源码解析(二十)处理acknack消息

FastDDS 源码解析(二十一)创建User的DataWriter(上)

FastDDS 源码解析(二十二)创建User的DataWriter(下)

上几篇,我们介绍了用户程序如何创建自己的DataWriter的

现在我们介绍一下用户程序如何创建自己的DataReader的

我们可以参考FastDDS 源码解析(二十一)创建User的DataWriter(上) 创建DataWriter和第一篇车载消息中间件FastDDS 源码解析(一)FastDDS 介绍和使用的代码例子,和第一篇相关内容比较类似先创建 subscriber

1.用户程序创建DataReader

1.1时序图

sequenceDiagram participant Application participant DomainParticipantFactory participant DomainParticipant participant DomainParticipantImpl participant TypeSupport participant Subscriber Application ->> DomainParticipantFactory:1.get_instance DomainParticipantFactory ->> DomainParticipantFactory: 2.create_participant DomainParticipantFactory ->> DomainParticipant: 3.new DomainParticipant DomainParticipantFactory ->> DomainParticipantImpl: 4.new DomainParticipantImpl DomainParticipantImpl ->> DomainParticipantFactory: return DomainParticipantImpl DomainParticipantFactory ->> DomainParticipant: 5.enable Application ->> TypeSupport:6.register_type Application ->> DomainParticipantImpl: 7.create_subscriber Application ->> DomainParticipantImpl: 8.create_topic Application ->> Subscriber: 9.create_dataReader
  1. DomainParticipantFactory::get_instance() 获取一个DomainParticipantFactory的对象

  2. DomainParticipantFactory 调用create_participant主要干了三件事情:

    a.new DomainParticipant() 见步骤3

    b.new DomainParticipantImpl() 在这里DomainParticipant 和DomainParticipantImpl对象是一一对应关系 见步骤4

    c.调用DomainParticipant对象的enable函数, 最终会调用到 DomainParticipantImpl对象 的enable函数,就是启动participant 见步骤5

  3. new 了一个DomainParticipant

  4. new了一个 DomainParticipantImpl()

  5. 调用DomainParticipant对象的enable函数

  6. 注册type,这个type 和 topic 是关联关系

  7. 只有同一个topic的2个participant 才能互相通信,通信内容的解析需要使用type解析

  8. 调用DomainParticipantImpl的create_subscriber函数,配置qos

  9. 调用DomainParticipantImpl的create_topic函数,配置qos

  10. 调用Publihser的create_datawriter函数,配置qos,发送消息就是由这个datawriter执行的

1.2源码

不再展开

参考第一篇车载消息中间件FastDDS 源码解析(一)FastDDS 介绍和使用接收端代码示例部分

2.创建Subscriber

sequenceDiagram participant User participant DomainParticipant participant DomainParticipantImpl participant Subscriber participant SubscriberImpl User ->> DomainParticipant: 1.create_subscriber DomainParticipant ->> DomainParticipantImpl: 2.create_subscriber DomainParticipantImpl ->> DomainParticipantImpl: 3.create_subscriber_impl DomainParticipantImpl ->> SubscriberImpl: 4.new DomainParticipantImpl ->> Subscriber: 5.new DomainParticipantImpl ->> Subscriber: 6.enable Subscriber ->> SubscriberImpl: 7.enable

1.用户程序调用DomainParticipant的create_subscriber函数,这个函数调用的是DomainParticipantImpl的create_subscriber函数

2.DomainParticipantImpl的create_subscriber函数 主要干了4件事

a.创建SubscriberImpl对象 见3

b.在SubscriberImpl对象的基础上创建Subscriber 见4

c.给SubscriberImpl对象分配instance_handle(guid) 见5

d.调用Subscriber对象的enable函数 见6

3.create_subscriber_impl 创建SubscriberImpl对象

4.创建SubscriberImpl对象

5.在SubscriberImpl的基础上创建Subscriber

6.调用Subscriber的enable函数

7.调用SubscriberImpl对象的enable函数

步骤1:

rust 复制代码
 Subscriber* DomainParticipant::create_subscriber(
         const SubscriberQos& qos,
         SubscriberListener* listener,
         const StatusMask& mask)
     return impl_->create_subscriber(qos, listener, mask);
 }

调用DomainParticipantImpl的create_subscriber

步骤2:

perl 复制代码
 Subscriber* DomainParticipantImpl::create_subscriber(
         const SubscriberQos& qos,
         SubscriberListener* listener,
         const StatusMask& mask)
 {
     if (!SubscriberImpl::check_qos(qos))
     {
         // The SubscriberImpl::check_qos() function is not yet implemented and always returns ReturnCode_t::RETCODE_OK.
         // It will be implemented in future releases of Fast DDS.
         // EPROSIMA_LOG_ERROR(PARTICIPANT, "SubscriberQos inconsistent or not supported");
         // return nullptr;
     }
 ​
     //TODO CONSTRUIR LA IMPLEMENTACION DENTRO DEL OBJETO DEL USUARIO.
     SubscriberImpl* subimpl = create_subscriber_impl(qos, listener);
     Subscriber* sub = new Subscriber(subimpl, mask);
     subimpl->user_subscriber_ = sub;
     subimpl->rtps_participant_ = get_rtps_participant();
 ​
     // Create InstanceHandle for the new subscriber
     InstanceHandle_t sub_handle;
     bool enabled = get_rtps_participant() != nullptr;
 ​
     // Create InstanceHandle for the new subscriber
     create_instance_handle(sub_handle);
     subimpl->handle_ = sub_handle;
 ​
     //SAVE THE PUBLISHER INTO MAPS
     std::lock_guard<std::mutex> lock(mtx_subs_);
     subscribers_by_handle_[sub_handle] = sub;
     subscribers_[sub] = subimpl;
 ​
     // Enable subscriber if appropriate
     if (enabled && qos_.entity_factory().autoenable_created_entities)
     {
         ReturnCode_t ret_subscriber_enable = sub->enable();
         assert(ReturnCode_t::RETCODE_OK == ret_subscriber_enable);
         (void)ret_subscriber_enable;
     }
 ​
     return sub;
 }

主要干了4件事

1.创建SubscriberImpl对象 见步骤3

2.在SubscriberImpl对象的基础上创建Subscriber 见步骤4

3.给SubscriberImpl对象分配instance_handle(guid) 见步骤5

4.调用Subscriber对象的enable函数 见步骤6

步骤3:

javascript 复制代码
 SubscriberImpl* DomainParticipantImpl::create_subscriber_impl(
         const SubscriberQos& qos,
         SubscriberListener* listener)
 {
     return new SubscriberImpl(this, qos, listener);
 }
 ​

步骤4-5没啥关键信息

步骤6:

ini 复制代码
 ReturnCode_t Subscriber::enable()
 {
     if (enable_)
     {
         return ReturnCode_t::RETCODE_OK;
     }
 ​
     if (false == impl_->get_participant()->is_enabled())
     {
         return ReturnCode_t::RETCODE_PRECONDITION_NOT_MET;
     }
 ​
     enable_ = true;
     ReturnCode_t ret_code = impl_->enable();
     enable_ = ReturnCode_t::RETCODE_OK == ret_code;
     return ret_code;
 }

主要是调用了SubscriberImpl的enable函数 见步骤7:

步骤7:

css 复制代码
 ReturnCode_t SubscriberImpl::enable()
 {
     if (qos_.entity_factory().autoenable_created_entities)
     {
         std::lock_guard<std::mutex> lock(mtx_readers_);
         for (auto topic_readers : readers_)
         {
             for (DataReaderImpl* dr : topic_readers.second)
             {
                 dr->user_datareader_->enable();
             }
         }
     }
 ​
     return ReturnCode_t::RETCODE_OK;
 }

调用SubscriberImpl中的DataReaderImpl的DataReader的enable函数

3.创建DataReader

sequenceDiagram participant User participant Subscriber participant SubscriberImpl participant DataReaderImpl participant DataReader participant RTPSDomainImpl participant RTPSParticipantImpl User ->> Subscriber:1.create_datareader Subscriber ->> SubscriberImpl:2.create_datareader SubscriberImpl ->> SubscriberImpl:3.create_datareader_impl SubscriberImpl ->> DataReaderImpl:4.new SubscriberImpl ->> DataReader:5.new SubscriberImpl ->> DataReader:6.enable DataReader ->> DataReaderImpl:7.enable DataReaderImpl ->> RTPSDomainImpl:8.create_rtps_reader RTPSDomainImpl ->> RTPSParticipantImpl:9.create_reader DataReaderImpl ->> RTPSParticipantImpl:10.registerReader

可以参考FastDDS 源码解析(二十一)创建User的DataWriter(上) 创建DataWriter,这里介绍的是创建DataReader,基本是类似的。

1.Subscriber的create_datareader函数调用的是SubscriberImpl的create_datareader函数

2.SubscriberImpl的create_datareader函数最终干了3件事

a.获取TypeSupport

b.调用create_datareader_impl函数来创建DataReaderImpl对象,传入参数TypeSupport对象 见步骤3

c.调用create_datareader创建 DataReader对象,传入参数DataReaderImpl对象 见步骤 5

d.调用DataReader的enable函数 见 步骤6

3.SubscriberImpl的create_datareader_impl函数,调用了DataReaderImpl的初始化函数 创建了一个DataReaderImpl的对象

4.new 了一个DataReaderImpl对象

5.new 了一个DataReader对象

6.DataReader对象的enable函数 调用的是DataReaderImpl对象的enable函数

7.DataReaderImpl对象的enable函数 主要干了2件事

a.调用RTPSDomainImpl的create_rtps_reader函数 见 8

b.调用RTPSParticipantImpl的registerReader函数 见 10

8.RTPSDomainImpl的create_rtps_reader函数主要是调用了RTPSParticipantImpl的create_reader函数

9.RTPSParticipantImpl的create_reader函数主要是创建了RTPSReader

10.RTPSParticipantImpl的registerReader函数主要是将reader的信息,存入PDP信息中,同时和writer进行匹配

步骤1:

javascript 复制代码
 DataReader* Subscriber::create_datareader(
         TopicDescription* topic,
         const DataReaderQos& reader_qos,
         DataReaderListener* listener,
         const StatusMask& mask)
 {
     return impl_->create_datareader(topic, reader_qos, listener, mask);
 }

调用的是SubscriberImpl的create_datareader函数

步骤2:

rust 复制代码
 DataReader* SubscriberImpl::create_datareader(
         TopicDescription* topic,
         const DataReaderQos& qos,
         DataReaderListener* listener,
         const StatusMask& mask)
 {
     EPROSIMA_LOG_INFO(SUBSCRIBER, "CREATING SUBSCRIBER IN TOPIC: " << topic->get_name());
     //Look for the correct type registration
     TypeSupport type_support = participant_->find_type(topic->get_type_name());
 ​
     /// Preconditions
     // Check the type was registered.
     if (type_support.empty())
     {
         EPROSIMA_LOG_ERROR(SUBSCRIBER, "Type : " << topic->get_type_name() << " Not Registered");
         return nullptr;
     }
 ​
     if (!DataReaderImpl::check_qos_including_resource_limits(qos, type_support))
     {
         return nullptr;
     }
 ​
     topic->get_impl()->reference();
 ​
     DataReaderImpl* impl = create_datareader_impl(type_support, topic, qos, listener);
     DataReader* reader = new DataReader(impl, mask);
     impl->user_datareader_ = reader;
 ​
     {
         std::lock_guard<std::mutex> lock(mtx_readers_);
         readers_[topic->get_name()].push_back(impl);
     }
 ​
     if (user_subscriber_->is_enabled() && qos_.entity_factory().autoenable_created_entities)
     {
         if (ReturnCode_t::RETCODE_OK != reader->enable())
         {
             delete_datareader(reader);
             return nullptr;
         }
     }
 ​
     return reader;
 }

主要干了3件事

1.获取TypeSupport

2.调用create_datareader_impl函数来创建DataReaderImpl对象,传入参数TypeSupport对象 见步骤3

3.new了一个DataReader对象,传入参数DataReaderImpl对象

4.DataReader对象的enable函数 见 步骤6

步骤3:

typescript 复制代码
 DataReaderImpl* SubscriberImpl::create_datareader_impl(
         const TypeSupport& type,
         TopicDescription* topic,
         const DataReaderQos& qos,
         DataReaderListener* listener)
 {
     return new DataReaderImpl(this, type, topic, qos, listener);
 }

步骤4 new DataReaderImpl 没什么关键代码

步骤5 new DataReader 没什么关键代码

步骤6

ini 复制代码
 ReturnCode_t DataReader::enable()
 {
     if (enable_)
     {
         return ReturnCode_t::RETCODE_OK;
     }
 ​
     if (false == impl_->get_subscriber()->is_enabled())
     {
         return ReturnCode_t::RETCODE_PRECONDITION_NOT_MET;
     }
 ​
     ReturnCode_t ret_code = impl_->enable();
     enable_ = ReturnCode_t::RETCODE_OK == ret_code;
     return ret_code;
 }

主要调用了DataReaderImpl的enable函数

步骤7

ini 复制代码
 ReturnCode_t DataReaderImpl::enable()
 {
     assert(reader_ == nullptr);
 ​
     ReaderAttributes att;
 ​
     att.endpoint.durabilityKind = qos_.durability().durabilityKind();
     att.endpoint.endpointKind = READER;
     att.endpoint.reliabilityKind = qos_.reliability().kind == RELIABLE_RELIABILITY_QOS ? RELIABLE : BEST_EFFORT;
     att.endpoint.topicKind = type_->m_isGetKeyDefined ? WITH_KEY : NO_KEY;
     att.endpoint.multicastLocatorList = qos_.endpoint().multicast_locator_list;
     att.endpoint.unicastLocatorList = qos_.endpoint().unicast_locator_list;
     att.endpoint.remoteLocatorList = qos_.endpoint().remote_locator_list;
     att.endpoint.external_unicast_locators = qos_.endpoint().external_unicast_locators;
     att.endpoint.ignore_non_matching_locators = qos_.endpoint().ignore_non_matching_locators;
     att.endpoint.properties = qos_.properties();
     att.endpoint.ownershipKind = qos_.ownership().kind;
     att.endpoint.setEntityID(qos_.endpoint().entity_id);
     att.endpoint.setUserDefinedID(qos_.endpoint().user_defined_id);
     att.times = qos_.reliable_reader_qos().times;
     att.liveliness_lease_duration = qos_.liveliness().lease_duration;
     att.liveliness_kind_ = qos_.liveliness().kind;
     att.matched_writers_allocation = qos_.reader_resource_limits().matched_publisher_allocation;
     att.expectsInlineQos = qos_.expects_inline_qos();
     att.disable_positive_acks = qos_.reliable_reader_qos().disable_positive_ACKs.enabled;
 ​
 ​
     // TODO(Ricardo) Remove in future
     // Insert topic_name and partitions
     Property property;
     property.name("topic_name");
     property.value(topic_->get_impl()->get_rtps_topic_name().c_str());
     att.endpoint.properties.properties().push_back(std::move(property));
 ​
     std::string* endpoint_partitions = PropertyPolicyHelper::find_property(qos_.properties(), "partitions");
 ​
     if (endpoint_partitions)
     {
         property.name("partitions");
         property.value(*endpoint_partitions);
         att.endpoint.properties.properties().push_back(std::move(property));
     }
     else if (subscriber_->get_qos().partition().names().size() > 0)
     {
         property.name("partitions");
         std::string partitions;
         bool is_first_partition = true;
         for (auto partition : subscriber_->get_qos().partition().names())
         {
             partitions += (is_first_partition ? "" : ";") + partition;
             is_first_partition = false;
         }
         property.value(std::move(partitions));
         att.endpoint.properties.properties().push_back(std::move(property));
     }
 ​
     bool is_datasharing_compatible = false;
     ReturnCode_t ret_code = check_datasharing_compatible(att, is_datasharing_compatible);
     if (ret_code != ReturnCode_t::RETCODE_OK)
     {
         return ret_code;
     }
     if (is_datasharing_compatible)
     {
         DataSharingQosPolicy datasharing(qos_.data_sharing());
         if (datasharing.domain_ids().empty())
         {
             datasharing.add_domain_id(utils::default_domain_id());
         }
         att.endpoint.set_data_sharing_configuration(datasharing);
     }
     else
     {
         DataSharingQosPolicy datasharing;
         datasharing.off();
         att.endpoint.set_data_sharing_configuration(datasharing);
     }
 ​
     std::shared_ptr<IPayloadPool> pool = get_payload_pool();
     RTPSReader* reader = RTPSDomain::createRTPSReader(
         subscriber_->rtps_participant(),
         guid_.entityId,
         att, pool,
         static_cast<ReaderHistory*>(&history_),
         static_cast<ReaderListener*>(&reader_listener_));
 ​
     if (reader == nullptr)
     {
         release_payload_pool();
         EPROSIMA_LOG_ERROR(DATA_READER, "Problem creating associated Reader");
         return ReturnCode_t::RETCODE_ERROR;
     }
 ​
     auto content_topic = dynamic_cast<ContentFilteredTopicImpl*>(topic_->get_impl());
     if (nullptr != content_topic)
     {
         reader->set_content_filter(content_topic);
         content_topic->add_reader(this);
     }
 ​
     reader_ = reader;
 ​
     deadline_timer_ = new TimedEvent(subscriber_->get_participant()->get_resource_event(),
                     [&]() -> bool
                     {
                         return deadline_missed();
                     },
                     qos_.deadline().period.to_ns() * 1e-6);
 ​
     lifespan_timer_ = new TimedEvent(subscriber_->get_participant()->get_resource_event(),
                     [&]() -> bool
                     {
                         return lifespan_expired();
                     },
                     qos_.lifespan().duration.to_ns() * 1e-6);
 ​
     // Register the reader
     ReaderQos rqos = qos_.get_readerqos(subscriber_->get_qos());
     if (!is_datasharing_compatible)
     {
         rqos.data_sharing.off();
     }
     if (endpoint_partitions)
     {
         std::istringstream partition_string(*endpoint_partitions);
         std::string partition_name;
         rqos.m_partition.clear();
 ​
         while (std::getline(partition_string, partition_name, ';'))
         {
             rqos.m_partition.push_back(partition_name.c_str());
         }
     }
 ​
     rtps::ContentFilterProperty* filter_property = nullptr;
     if (nullptr != content_topic && !content_topic->filter_property.filter_expression.empty())
     {
         filter_property = &content_topic->filter_property;
     }
     if (!subscriber_->rtps_participant()->registerReader(reader_, topic_attributes(), rqos, filter_property))
     {
         EPROSIMA_LOG_ERROR(DATA_READER, "Could not register reader on discovery protocols");
 ​
         reader_->setListener(nullptr);
         stop();
 ​
         return ReturnCode_t::RETCODE_ERROR;
     }
 ​
     return ReturnCode_t::RETCODE_OK;
 }
 ​

主要干了这几件事情

1.调用RTPSDomainImpl::create_rtps_reader创建了一个RTPSReader,这是真正发送消息的对象 见步骤8

RTPSReader的ReaderHistory和DataReaderImpl的DataReaderHistory是共用的,DataReaderHistory是ReaderHistory的子类

2.创建了2个TimedEvent

deadline_timer_

fastdds 有个qos 叫deadlineqos ,这个qos的参数一般是个周期性的时间,规定了在周期性的时间内,发送端必须发送一个消息出来。

在接收端在这个周期性的时间内需要收到一个消息。

lifespan_timer_

fastdds有个qos 叫lifespanqos,这个qos规定了,一个消息的存活时间,超过时间,会在发送端将消息移除,这个默认设置,会把存货时间设置为无限。

3.调用RTPSParticipant::registerReader,注册这个RTPSReader 见步骤10

步骤8:

rust 复制代码
 RTPSReader* RTPSDomain::createRTPSReader(
         RTPSParticipant* p,
         const EntityId_t& entity_id,
         ReaderAttributes& ratt,
         const std::shared_ptr<IPayloadPool>& payload_pool,
         ReaderHistory* rhist,
         ReaderListener* rlisten)
 {
     RTPSParticipantImpl* impl = p->mp_impl;
     if (impl)
     {
         RTPSReader* reader;
         if (impl->createReader(&reader, ratt, payload_pool, rhist, rlisten, entity_id))
         {
             return reader;
         }
     }
     return nullptr;
 }

调用RTPSParticipantImpl对象的create_reader,来创建RTPSReader对象,如果是reliable的RTPSReader,就是StatefulReader,如果是besteffort的RTPSReader,就是StatlessReader。

这个函数我们在之前介绍过,创建RTPSReader,创建ReceiverResources,将ReceiverResources的MessageReceiver 与RTPSReader关联,当MessageReceiver收到消息后,将会分配给相应的RTPSWriter或RTPSReader,让他们进行消息处理

步骤10:

这块内容,我们在下一篇介绍

总结:我们看到,创建DataReader的过程和之前创建DataWriter的过程基本是一致的,略有不同

2.3类图

classDiagram DomainParticipantImpl *-- SubscriberImpl DomainParticipantImpl *-- Subscriber Subscriber *-- SubscriberImpl SubscriberImpl *-- DataReaderImpl DataReaderImpl *-- RTPSReader class DomainParticipantImpl { +PublisherQos default_pub_qos_ +std::map publishers_ +SubscriberQos default_sub_qos_ +std::map subscribers_ } class SubscriberImpl { +std::map> readers_ } class Subscriber { +SubscriberImpl* mp_impl } class DataReaderImpl { +RTPSReader reader_ }
  1. DomainParticipantImpl管理所有的Subscriber对象

    Subscriber 和 SubscriberImpl 存储在一个map中( std::map<Subscriber, SubscriberImpl> publishers_ )

  2. Subscriber 类似于 SubscriberImpl的代理类,Subscriber 和 SubscriberImpl 是一一对应的。

  3. SubscriberImpl管理着DataReaderImpl对象。

  4. DataReader 和 RTPSReader 是一一对应的。

    这块代码我理解有些部分是有点冗余的,Subscriber 和 SubscriberImpl,DataReader和DataReaderImpl没必要分成2个类。

第一篇 一个例子

车载消息中间件FastDDS 源码解析(一)FastDDS 介绍和使用

第二篇fastdds的组成

车载消息中间件FastDDS 源码解析(二)RtpsParticipant的创建(上)

车载消息中间件FastDDS 源码解析(三)RtpsParticipant的创建(中)

车载消息中间件FastDDS 源码解析(四)RtpsParticipant的创建(下)

车载消息中间件FastDDS 源码解析(五)BuiltinProtocols(上)

车载消息中间件FastDDS 源码解析(六)BuiltinProtocols(中)EDP

车载消息中间件FastDDS 源码解析(七)BuiltinProtocols(下)WLP&TypeLookupManager

车载消息中间件FastDDS 源码解析(八)TimedEvent

车载消息中间件FastDDS 源码解析(九)Message

第三篇组网建立连接

pdp建连

车载消息中间件FastDDS 源码解析(十)发送第一条PDP消息(上)

FastDDS 源码解析(十一)发送第一条PDP消息(中)

FastDDS 源码解析(十二)发送第一条PDP消息(下)---异步发送

FastDDS 源码解析(十三)发送第一条PDP消息---跨进程发送

FastDDS 源码解析(十四)接收PDP消息(上)

FastDDS 源码解析(十五)接收PDP消息(下)

FastDDS 源码解析(十六)处理PDP消息------PDP匹配

EDP建连

FastDDS 源码解析(十七)处理PDP消息------EDP匹配

FastDDS 源码解析(十八)EDP阶段发送心跳heartbeat

FastDDS 源码解析(十九)EDP阶段处理heartbeat消息,发送acknack消息

FastDDS 源码解析(二十)处理acknack消息

FastDDS 源码解析(二十一)创建User的DataWriter(上)

FastDDS 源码解析(二十二)创建User的DataWriter(下)

相关推荐
该叫啥37 分钟前
SpringBoot与Redisson整合,用注解方式解决分布式锁的使用问题
spring boot·redis·分布式·后端
WLKQ2 小时前
zookeeper使用
分布式·zookeeper·云原生
楼台的春风2 小时前
【Harris角点检测器详解】
图像处理·人工智能·深度学习·opencv·算法·计算机视觉·嵌入式
莫等闲!2 小时前
kafka的文章
分布式·kafka
Cloud_.4 小时前
RabbitMQ 详细原理解析
分布式·rabbitmq
王小小海5 小时前
【原创首发】开源基于AT32 SIP/VOIP电话
stm32·单片机·网络协议·开源·嵌入式
FreakStudio9 小时前
一文速通Python并行计算:02 Python多线程编程-threading模块、线程的创建和查询与守护线程
python·嵌入式·多线程·并行计算·电子diy
牛亚肖11 小时前
Plant Simulation中怎么更改机器人3D模型
笔记·机器人
sheji10511 小时前
WRC世界机器人大会-2024年展商汇总
机器人
爱尔兰的楠小楠13 小时前
ROS多机通信(四)——Ubuntu 网卡 Mesh 模式配置指南
linux·分布式·ubuntu·机器人·去中心化