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

381 阅读16分钟

第一篇 一个例子

车载消息中间件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(上)

我们上一篇介绍了创建用户自己的DataWriter

2.注册DataWriter

这一篇我们介绍一下注册DataWriter,为什么需要注册这个DataWriter,这个注册是向EDP注册一下,这个DataWriter的信息。

EDP会把这个信息发送给对端的EDP,这样对端就会知道有这个DataWriter,如果远端有可以匹配的DataReader,那么这两个

DataWriter 和 DataReader可以互相发送消息,发送heartbeat ,acknack消息和具体的消息。

这一篇就是介绍一下这个过程

2.1时序图

sequenceDiagram
participant DataWriterImpl
		participant RTPSParticipantImpl
		participant BuiltinProtocols
		Participant EDP
		Participant PDP 
		 
		DataWriterImpl ->> RTPSParticipantImpl:1.registerWriter
		RTPSParticipantImpl ->> BuiltinProtocols:2.addLocalWriter
		BuiltinProtocols ->> EDP:3.newLocalWriterProxyData
		EDP ->> PDP:4.addWriterProxyData
		EDP ->> EDP:5.pairing_writer_proxy_with_any_local_reader
		EDP ->> EDP:6.pairingWriter
		EDP ->> EDPSimple:7.processLocalWriterProxyData
		BuiltinProtocols ->> WLP:8.add_local_writer

1.RTPSParticipant的registerWriter函数 调用了RTPSParticipantImpl的registerWriter来注册这个writer

RTPSParticipantImpl的registerWriter调用了BuiltinProtocols的addLocalWriter

2.BuiltinProtocols的addLocalWriter主要干了2件事

a.BuiltinProtocols的newLocalWriterProxyData 见步骤3

b.调用WLP的add_local_writer。见步骤8:

3.BuiltinProtocols的newLocalWriterProxyData主要干了4件事

a.调用 PDP::addWriterProxyData 见步骤4

这儿有一个lamanda函数 init_fun,这个作为参数传递给addWriterProxyData

b.调用 pairing_writer_proxy_with_any_local_reader,与本地的reader进行匹配 见步骤5

c.调用 pairingWriter 与已经知道的远端的Reader 匹配 见步骤6

d.调用 processLocalWriterProxyData 向远端的Reader发送消息 见步骤7

4.PDP的addWriterProxyData

先查找participant_proxies_,找到这个writer对应的ParticipantProxyData,在这个ParticipantProxyData中查找对应的WriterProxyData,找到就更新WriterProxyData

没有找到就新建一个WriterProxyData,将WriterProxyData信息存入ParticipantProxyData

5.查看本地的UserReader,看一下Reader 和这个新建的writer是否匹配(reader和writer的很多qos是否一致)

如果匹配将writer加入到reader匹配的writer的队列中。这个是本地reader的匹配

6.遍历了participant_proxies_,找到其中的reader,与我们的writer进行匹配。

如果匹配将writer加入到reader匹配的writer的队列中。这个主要是远端reader的匹配。

7.主要是将writer 的信息,序列化后发送给远端

8.WLP来管理我们这个writer的liveliness信息

2.2源码解析

步骤1:

 bool RTPSParticipant::registerWriter(
         RTPSWriter* Writer,
         const TopicAttributes& topicAtt,
         const WriterQos& wqos)
 {
     return mp_impl->registerWriter(Writer, topicAtt, wqos);
 }

调用RTPSParticipantImpl::registerWriter来注册这个writer

 bool RTPSParticipantImpl::registerWriter(
         RTPSWriter* Writer,
         const TopicAttributes& topicAtt,
         const WriterQos& wqos)
 {
     return this->mp_builtinProtocols->addLocalWriter(Writer, topicAtt, wqos);
 }

调用BuiltinProtocols::addLocalWriter

步骤2:

 bool BuiltinProtocols::addLocalWriter(
         RTPSWriter* w,
         const fastrtps::TopicAttributes& topicAtt,
         const fastrtps::WriterQos& wqos)
 {
     bool ok = true;
 ​
     if (mp_PDP != nullptr)
     {
         ok = mp_PDP->getEDP()->newLocalWriterProxyData(w, topicAtt, wqos);
 ​
         if (!ok)
         {
             EPROSIMA_LOG_WARNING(RTPS_EDP, "Failed register WriterProxyData in EDP");
             return false;
         }
     }
     ------
 ​
     if (mp_WLP != nullptr)
     {
         ok &= mp_WLP->add_local_writer(w, wqos);
     }
     ------
     return ok;
 }
 ​

主要是干了两件事

1.调用EDP::newLocalWriterProxyData。见步骤3:

2.调用WLP的add_local_writer。见步骤8:

因为是新建了一个RTPSWriter,所以这些信息需要通过本地EDP,发送给远端的EDP端点

步骤3:

 bool EDP::newLocalWriterProxyData(
         RTPSWriter* writer,
         const TopicAttributes& att,
         const WriterQos& wqos)
 {
     EPROSIMA_LOG_INFO(RTPS_EDP, "Adding " << writer->getGuid().entityId << " in topic " << att.topicName);
 ​
     auto init_fun = [this, writer, &att, &wqos](
         WriterProxyData* wpd,
         bool updating,
         const ParticipantProxyData& participant_data)
             {
                 if (updating)
                 {
                     EPROSIMA_LOG_ERROR(RTPS_EDP,
                             "Adding already existent writer " << writer->getGuid().entityId << " in topic "
                                                               << att.topicName);
                     return false;
                 }
 ​
                 const NetworkFactory& network = mp_RTPSParticipant->network_factory();
                 const auto& watt = writer->getAttributes();
 ​
                 wpd->guid(writer->getGuid());
                 wpd->key() = wpd->guid();
                 if (watt.multicastLocatorList.empty() && watt.unicastLocatorList.empty())
                 {
                     wpd->set_locators(participant_data.default_locators);
                 }
                 else
                 {
                     wpd->set_multicast_locators(watt.multicastLocatorList, network);
                     wpd->set_announced_unicast_locators(watt.unicastLocatorList);
                     fastdds::rtps::ExternalLocatorsProcessor::add_external_locators(*wpd,
                             watt.external_unicast_locators);
                 }
                 wpd->RTPSParticipantKey() = mp_RTPSParticipant->getGuid();
                 wpd->topicName(att.getTopicName());
                 wpd->typeName(att.getTopicDataType());
                 wpd->topicKind(att.getTopicKind());
                 if (att.type_id.m_type_identifier._d() != static_cast<uint8_t>(0x00))
                 {
                     wpd->type_id(att.type_id);
                 }
                 if (att.type.m_type_object._d() != static_cast<uint8_t>(0x00))
                 {
                     wpd->type(att.type);
                 }
                 if (att.type_information.assigned())
                 {
                     wpd->type_information(att.type_information);
                 }
                 wpd->typeMaxSerialized(writer->getTypeMaxSerialized());
                 wpd->m_qos.setQos(wqos, true);
                 wpd->userDefinedId(watt.getUserDefinedID());
                 wpd->persistence_guid(watt.persistence_guid);
 #if HAVE_SECURITY
                 if (mp_RTPSParticipant->is_secure())
                 {
                     wpd->security_attributes_ = watt.security_attributes().mask();
                     wpd->plugin_security_attributes_ = watt.security_attributes().plugin_endpoint_attributes;
                 }
                 else
                 {
                     wpd->security_attributes_ = 0UL;
                     wpd->plugin_security_attributes_ = 0UL;
                 }
 #endif // if HAVE_SECURITY
 ​
                 if (att.auto_fill_type_information)
                 {
                     // TypeInformation, TypeObject and TypeIdentifier
                     if (!att.type_information.assigned())
                     {
                         const types::TypeInformation* type_info =
                                 types::TypeObjectFactory::get_instance()->get_type_information(wpd->typeName().c_str());
                         if (type_info != nullptr)
                         {
                             wpd->type_information() = *type_info;
                         }
                     }
                 }
 ​
                 if (att.auto_fill_type_object)
                 {
                     bool has_type_id = true;
                     if (att.type_id.m_type_identifier._d() == static_cast<uint8_t>(0x00))
                     {
                         has_type_id = false;
                         const types::TypeIdentifier* type_id =
                                 types::TypeObjectFactory::get_instance()->get_type_identifier_trying_complete(
                             wpd->typeName().c_str());
                         if (type_id != nullptr)
                         {
                             has_type_id = true;
                             wpd->type_id().m_type_identifier = *type_id;
                         }
                     }
 ​
                     if (att.type.m_type_object._d() == static_cast<uint8_t>(0x00))
                     {
                         bool type_is_complete = has_type_id &&
                                 wpd->type_id().m_type_identifier._d() == types::EK_COMPLETE;
                         const types::TypeObject* type_obj =
                                 types::TypeObjectFactory::get_instance()->get_type_object(
                             wpd->typeName().c_str(), type_is_complete);
                         if (type_obj != nullptr)
                         {
                             wpd->type().m_type_object = *type_obj;
                         }
                     }
                 }
 ​
                 return true;
             };
 ​
     //ADD IT TO THE LIST OF READERPROXYDATA
     GUID_t participant_guid;
     WriterProxyData* writer_data = this->mp_PDP->addWriterProxyData(writer->getGuid(), participant_guid, init_fun);
     if (writer_data == nullptr)
     {
         return false;
     }
 ​
     //PAIRING
     if (this->mp_PDP->getRTPSParticipant()->should_match_local_endpoints())
     {
         pairing_writer_proxy_with_any_local_reader(participant_guid, writer_data);
     }
     pairingWriter(writer, participant_guid, *writer_data);
     //DO SOME PROCESSING DEPENDING ON THE IMPLEMENTATION (SIMPLE OR STATIC)
     processLocalWriterProxyData(writer, writer_data);
     return true;
 }

主要干了4件事

1.调用 PDP::addWriterProxyData 见步骤4

这儿有一个lamanda函数 init_fun,这个作为参数传递给addWriterProxyData

2.调用 pairing_writer_proxy_with_any_local_reader,与本地的reader进行匹配 见步骤5

3.调用 pairingWriter 与已经知道的远端的Reader 匹配 见步骤6

4.调用 processLocalWriterProxyData 向远端的Reader发送消息 见步骤7

步骤4:

 WriterProxyData* PDP::addWriterProxyData(
         const GUID_t& writer_guid,
         GUID_t& participant_guid,
         std::function<bool(WriterProxyData*, bool, const ParticipantProxyData&)> initializer_func)
 {
     EPROSIMA_LOG_INFO(RTPS_PDP, "Adding writer proxy data " << writer_guid);
     WriterProxyData* ret_val = nullptr;
 ​
     // notify statistics module
     getRTPSParticipant()->on_entity_discovery(writer_guid, ParameterPropertyList_t());
 ​
     std::lock_guard<std::recursive_mutex> guardPDP(*this->mp_mutex);
 ​
     for (ParticipantProxyData* pit : participant_proxies_)
     {
         if (pit->m_guid.guidPrefix == writer_guid.guidPrefix)
         {
             // Copy participant data to be used outside.
             participant_guid = pit->m_guid;
 ​
             // Check that it is not already there:
             // 找一下之前有没有创建过这个WriterProxyData
             auto wpi = pit->m_writers->find(writer_guid.entityId);
 ​
             if (wpi != pit->m_writers->end())
             {
                 ret_val = wpi->second;
                 //为WriterProxyData对象配置参数
                 if (!initializer_func(ret_val, true, *pit))
                 {
                     return nullptr;
                 }
 ​
                 RTPSParticipantListener* listener = mp_RTPSParticipant->getListener();
                 if (listener)
                 {
                     WriterDiscoveryInfo info(*ret_val);
                     info.status = WriterDiscoveryInfo::CHANGED_QOS_WRITER;
                     listener->onWriterDiscovery(mp_RTPSParticipant->getUserRTPSParticipant(), std::move(info));
                     check_and_notify_type_discovery(listener, *ret_val);
                 }
 ​
                 return ret_val;
             }
 ​
             // Try to take one entry from the pool
             if (writer_proxies_pool_.empty())
             {
                 size_t max_proxies = writer_proxies_pool_.max_size();
                 if (writer_proxies_number_ < max_proxies)
                 {
                     // Pool is empty but limit has not been reached, so we create a new entry.
                     ++writer_proxies_number_;
                     ret_val = new WriterProxyData(
                         mp_RTPSParticipant->getAttributes().allocation.locators.max_unicast_locators,
                         mp_RTPSParticipant->getAttributes().allocation.locators.max_multicast_locators,
                         mp_RTPSParticipant->getAttributes().allocation.data_limits);
                 }
                 else
                 {
                     EPROSIMA_LOG_WARNING(RTPS_PDP, "Maximum number of writer proxies (" << max_proxies <<
                             ") reached for participant " << mp_RTPSParticipant->getGuid() << std::endl);
                     return nullptr;
                 }
             }
             else
             {
                 // Pool is not empty, use entry from pool
                 ret_val = writer_proxies_pool_.back();
                 writer_proxies_pool_.pop_back();
             }
 ​
             // Add to ParticipantProxyData
             (*pit->m_writers)[writer_guid.entityId] = ret_val;
 ​
             if (!initializer_func(ret_val, false, *pit))
             {
                 return nullptr;
             }
 ​
             RTPSParticipantListener* listener = mp_RTPSParticipant->getListener();
             if (listener)
             {
                 WriterDiscoveryInfo info(*ret_val);
                 info.status = WriterDiscoveryInfo::DISCOVERED_WRITER;
                 listener->onWriterDiscovery(mp_RTPSParticipant->getUserRTPSParticipant(), std::move(info));
                 check_and_notify_type_discovery(listener, *ret_val);
             }
 ​
             return ret_val;
         }
     }
 ​
     return nullptr;
 }

这个函数主要是为了创建WriterProxyData的对象,配置参数等

先查找participant_proxies_,找到这个writer对应的ParticipantProxyData,在这个ParticipantProxyData中查找对应的WriterProxyData,找到就更新WriterProxyData

没有找到就新建一个WriterProxyData,将WriterProxyData信息存入ParticipantProxyData

步骤5:

 bool EDP::pairing_writer_proxy_with_any_local_reader(
         const GUID_t& participant_guid,
         WriterProxyData* wdata)
 {
     (void)participant_guid;
 ​
     EPROSIMA_LOG_INFO(RTPS_EDP, wdata->guid() << " in topic: "" << wdata->topicName() << """);
 ​
     mp_RTPSParticipant->forEachUserReader([&, wdata](RTPSReader& r) -> bool
             {
                 auto temp_reader_proxy_data = get_temporary_reader_proxies_pool().get();
                 GUID_t readerGUID = r.getGuid();
 ​
                 if (mp_PDP->lookupReaderProxyData(readerGUID, *temp_reader_proxy_data))
                 {
                     MatchingFailureMask no_match_reason;
                     fastdds::dds::PolicyMask incompatible_qos;
                     // 这个函数主要是看reader 和writer是否匹配
                     
                     bool valid = valid_matching(temp_reader_proxy_data.get(), wdata, no_match_reason, incompatible_qos);
                     const GUID_t& writer_guid = wdata->guid();
 ​
                     temp_reader_proxy_data.reset();
                     //如果匹配
                     if (valid)
                     {
 #if HAVE_SECURITY
                         if (!mp_RTPSParticipant->security_manager().discovered_writer(readerGUID, participant_guid,
                         *wdata, r.getAttributes().security_attributes()))
                         {
                             EPROSIMA_LOG_ERROR(RTPS_EDP, "Security manager returns an error for reader " << readerGUID);
                         }
 #else
                         //将writer加入到reader匹配的writer的队列中
                         if (r.matched_writer_add(*wdata))
                         {
                             EPROSIMA_LOG_INFO(RTPS_EDP_MATCH,
                             "WP:" << wdata->guid() << " match R:" << r.getGuid() << ". WLoc:" <<
                                 wdata->remote_locators());
                             //MATCHED AND ADDED CORRECTLY:
                             if (r.getListener() != nullptr)
                             {
                                 MatchingInfo info;
                                 info.status = MATCHED_MATCHING;
                                 info.remoteEndpointGuid = writer_guid;
                                 r.getListener()->onReaderMatched(&r, info);
 ​
                                 const SubscriptionMatchedStatus& sub_info =
                                 update_subscription_matched_status(readerGUID, writer_guid, 1);
                                 r.getListener()->onReaderMatched(&r, sub_info);
                             }
                         }
 #endif // if HAVE_SECURITY
                     }
                     else
                     {
                         if (no_match_reason.test(MatchingFailureMask::incompatible_qos) && r.getListener() != nullptr)
                         {
                             r.getListener()->on_requested_incompatible_qos(&r, incompatible_qos);
                         }
                         //如果这个writer之前被匹配过,现在这个writer又不匹配了,表明这个writer的qos有变化,需要将这个                             writer从匹配的队列中移除
                         if (r.matched_writer_is_matched(writer_guid)
                         && r.matched_writer_remove(writer_guid))
                         {
 #if HAVE_SECURITY
                             mp_RTPSParticipant->security_manager().remove_writer(readerGUID, participant_guid,
                             writer_guid);
 #endif // if HAVE_SECURITY
                             //MATCHED AND ADDED CORRECTLY:
                             if (r.getListener() != nullptr)
                             {
                                 MatchingInfo info;
                                 info.status = REMOVED_MATCHING;
                                 info.remoteEndpointGuid = writer_guid;
                                 r.getListener()->onReaderMatched(&r, info);
 ​
                                 const SubscriptionMatchedStatus& sub_info =
                                 update_subscription_matched_status(readerGUID, writer_guid, -1);
                                 r.getListener()->onReaderMatched(&r, sub_info);
                             }
                         }
                     }
                 }
                 // keep looking
                 return true;
             });
 ​
     return true;
 }

查看本地的UserReader,调用valid_matching 看一下Reader 和这个新建的writer是否匹配(reader和writer的很多qos是否一致)

如果匹配matched_writer_add函数将writer加入到reader匹配的writer的队列中,(这块可以参考FastDDS 源码解析(十六)处理PDP消息——PDP匹配 PDP writer和reader的匹配)。

步骤6:

 bool EDP::pairingWriter(
         RTPSWriter* W,
         const GUID_t& participant_guid,
         const WriterProxyData& wdata)
 {
     (void)participant_guid;
 ​
     EPROSIMA_LOG_INFO(RTPS_EDP, W->getGuid() << " in topic: "" << wdata.topicName() << """);
     std::lock_guard<std::recursive_mutex> pguard(*mp_PDP->getMutex());
 ​
     ResourceLimitedVector<ParticipantProxyData*>::const_iterator pit = mp_PDP->ParticipantProxiesBegin();
     if (!this->mp_PDP->getRTPSParticipant()->should_match_local_endpoints())
     {
         pit++;
     }
 ​
     //从匹配的participant 中寻找reader,与writer进行匹配
     for (; pit != mp_PDP->ParticipantProxiesEnd(); ++pit)
     {   
         
         for (auto& pair : *(*pit)->m_readers)
         {
             ReaderProxyData* rdatait = pair.second;
             const GUID_t& reader_guid = rdatait->guid();
             if (reader_guid == c_Guid_Unknown)
             {
                 continue;
             }
 ​
             MatchingFailureMask no_match_reason;
             fastdds::dds::PolicyMask incompatible_qos;
             //与writer进行匹配
             bool valid = valid_matching(&wdata, rdatait, no_match_reason, incompatible_qos);
 ​
             if (valid)
             {
 #if HAVE_SECURITY
                 if (!mp_RTPSParticipant->security_manager().discovered_reader(W->getGuid(), (*pit)->m_guid,
                         *rdatait, W->getAttributes().security_attributes()))
                 {
                     EPROSIMA_LOG_ERROR(RTPS_EDP, "Security manager returns an error for writer " << W->getGuid());
                 }
 #else
                 //如果匹配成功,将reader,加入到writer 的队列中
                 if (W->matched_reader_add(*rdatait))
                 {
                     EPROSIMA_LOG_INFO(RTPS_EDP_MATCH,
                             "RP:" << rdatait->guid() << " match W:" << W->getGuid() << ". WLoc:" <<
                             rdatait->remote_locators());
                     //MATCHED AND ADDED CORRECTLY:
                     if (W->getListener() != nullptr)
                     {
                         MatchingInfo info;
                         info.status = MATCHED_MATCHING;
                         info.remoteEndpointGuid = reader_guid;
                         W->getListener()->onWriterMatched(W, info);
 ​
                         const GUID_t& writer_guid = W->getGuid();
                         const PublicationMatchedStatus& pub_info =
                                 update_publication_matched_status(reader_guid, writer_guid, 1);
                         W->getListener()->onWriterMatched(W, pub_info);
                     }
                 }
 #endif // if HAVE_SECURITY
             }
             else
             {
                 if (no_match_reason.test(MatchingFailureMask::incompatible_qos) && W->getListener() != nullptr)
                 {
                     W->getListener()->on_offered_incompatible_qos(W, incompatible_qos);
                 }
 ​
                 //EPROSIMA_LOG_INFO(RTPS_EDP,RTPS_CYAN<<"Valid Matching to writerProxy: "<<wdatait->m_guid<<RTPS_DEF<<endl);
                 if (W->matched_reader_is_matched(reader_guid) && W->matched_reader_remove(reader_guid))
                 {
 #if HAVE_SECURITY
                     mp_RTPSParticipant->security_manager().remove_reader(W->getGuid(), participant_guid, reader_guid);
 #endif // if HAVE_SECURITY
                     //MATCHED AND ADDED CORRECTLY:
                     if (W->getListener() != nullptr)
                     {
                         MatchingInfo info;
                         info.status = REMOVED_MATCHING;
                         info.remoteEndpointGuid = reader_guid;
                         W->getListener()->onWriterMatched(W, info);
 ​
                         const GUID_t& writer_guid = W->getGuid();
                         const PublicationMatchedStatus& pub_info =
                                 update_publication_matched_status(reader_guid, writer_guid, -1);
                         W->getListener()->onWriterMatched(W, pub_info);
 ​
                     }
                 }
             }
         }
     }
     return true;
 }

这个函数主要从匹配的participant 中寻找reader,与writer进行匹配

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

我们可以看到每个pdp中都会把匹配的Participant的信息,存入到participant_proxies_ ,本地的participant的信息,作为第一个ParticipantProxyData,存入到 participant_proxies_ ,远程的Participant的信息,也会存入到 participant_proxies_中去。

这个函数遍历了participant_proxies_,找到其中的reader,与我们的writer进行匹配。

如果匹配将writer加入到reader匹配的writer的队列中。

步骤7:

 bool EDPSimple::processLocalWriterProxyData(
         RTPSWriter* local_writer,
         WriterProxyData* wdata)
 {
     EPROSIMA_LOG_INFO(RTPS_EDP, wdata->guid().entityId);
     (void)local_writer;
 ​
     auto* writer = &publications_writer_;
 ​
 #if HAVE_SECURITY
     if (local_writer->getAttributes().security_attributes().is_discovery_protected)
     {
         writer = &publications_secure_writer_;
     }
 #endif // if HAVE_SECURITY
 ​
     CacheChange_t* change = nullptr;
     bool ret_val = serialize_writer_proxy_data(*wdata, *writer, true, &change);
     if (change != nullptr)
     {
         //这是个关键的发送自身节点信息给到远端,
         writer->second->add_change(change);
     }
     return ret_val;
 }

这个函数主要是将writer 的信息,序列化后发送给远端

publications_writer_是发送者,信息是这个WriterProxyData,这里面就是将WriterProxyData中的信息,转化为一个CacheChange_t对象,然后调用WriterHistory的add_change函数

2.3类图

classDiagram
PDP *-- ParticipantProxyData
      ParticipantProxyData *-- ReaderProxyData
      ParticipantProxyData *-- WriterProxyData
      class PDP {
      		+ResourceLimitedVector<ParticipantProxyData*> participant_proxies_
      }
      class ParticipantProxyData {
      		+ProxyHashTable<ReaderProxyData>* m_readers = nullptr;
    			+ProxyHashTable<WriterProxyData>* m_writers = nullptr;
      }
      

1.我们看到PDP中存放着所有这个PDP知道的ParticipantProxyData,远端匹配的Participant的ParticipantProxyData,自己的Participant的ParticipantProxyData

2.ParticipantProxyData又包含这个Participantz中的Reader的ReaderProxyData 和WriterProxyData

3.发送消息

3.1源码

我们之前在发送pdp消息的时候(参考车载消息中间件FastDDS 源码解析(十)发送第一条PDP消息(上))介绍过如何发送消息,这边发送的是一条EDP消息,略有点不一样,我们可以参考一下,思考一下这部分有什么不一样。

sequenceDiagram
participant EDPSimple
		participant WriterHistory
		participant StatefulWriter
		participant FlowControllerImpl
		EDPSimple ->> WriterHistory: 1.add_change
		WriterHistory ->> WriterHistory: 2.add_change_
		WriterHistory ->> WriterHistory: 3.prepare_and_add_change
		WriterHistory ->> WriterHistory: 4.notify_writer
		WriterHistory ->> StatefulWriter: 5.unsent_change_added_to_history
		StatefulWriter ->> FlowControllerImpl: 6.add_new_sample

1.RTPSWriter::new_change 创建了一个change,并配置相关参数

2.WriterHistory::add_change 调用WriterHistory::add_change_

3.WriterHistory::add_change_干了2件事

a.调用WriterHistory::prepare_and_add_change 见4

b.调用WriterHistory::notify_writer 见5

4.WriterHistory::prepare_and_add_change 给每个新change 一个sequenceNumber,然后将change存入

5.WriterHistory::notify_writer 调用statefulwriter 的 unsent_change_added_to_history函数

6.statefulwriter 的 unsent_change_added_to_history函数 调用FlowControllerimpl的add_new_sample

1-5的源码可以参考车载消息中间件FastDDS 源码解析(十)发送第一条PDP消息(上)第2章

我们看一下StatefulWriter::unsent_change_added_to_history的源码部分:

 void StatefulWriter::unsent_change_added_to_history(
         CacheChange_t* change,
         const std::chrono::time_point<std::chrono::steady_clock>& max_blocking_time)
 {
     std::lock_guard<RecursiveTimedMutex> guard(mp_mutex);
     auto payload_length = change->serializedPayload.length;
 ​
     if (liveliness_lease_duration_ < c_TimeInfinite)
     {
         mp_RTPSParticipant->wlp()->assert_liveliness(
             getGuid(),
             liveliness_kind_,
             liveliness_lease_duration_);
     }
 ​
     // Prepare the metadata for datasharing
     if (is_datasharing_compatible())
     {
         prepare_datasharing_delivery(change);
     }
 ​
     // Now for the rest of readers
     if (!matched_remote_readers_.empty() || !matched_datasharing_readers_.empty() || !matched_local_readers_.empty())
     {
         bool should_be_sent = false;
         for_matched_readers(matched_local_readers_, matched_datasharing_readers_, matched_remote_readers_,
                 [this, &should_be_sent, &change, &max_blocking_time](ReaderProxy* reader)
                 {
                     ChangeForReader_t changeForReader(change);
                     bool is_revelant = reader->rtps_is_relevant(change);
 ​
                     if (m_pushMode || !reader->is_reliable() || reader->is_local_reader())
                     {
                         //ChangeForReader_t construct sets status to UNSENT.
                         should_be_sent |= is_revelant;
                     }
                     else
                     {
                         changeForReader.setStatus(UNACKNOWLEDGED);
                     }
                     //远端的reader history中加入change 
                     reader->add_change(changeForReader, is_revelant, false, max_blocking_time);
 ​
                     return false;
                 }
                 );
 ​
         //不需要positive_ack的处理逻辑
         if (disable_positive_acks_)
         {
             auto source_timestamp = system_clock::time_point() + nanoseconds(change->sourceTimestamp.to_ns());
             auto now = system_clock::now();
             auto interval = source_timestamp - now + keep_duration_us_;
             assert(interval.count() >= 0);
 ​
             ack_event_->update_interval_millisec((double)duration_cast<milliseconds>(interval).count());
             ack_event_->restart_timer(max_blocking_time);
         }
 ​
         // After adding the CacheChange_t to flowcontroller, its pointer cannot be used because it may be removed
         // internally before exiting the call. For example if the writer matched with a best-effort reader.
         if (should_be_sent)
         {
             //flow_controller_ 发送change
             flow_controller_->add_new_sample(this, change, max_blocking_time);
         }
         else
         {
             periodic_hb_event_->restart_timer(max_blocking_time);
         }
     }
     else
     {
         EPROSIMA_LOG_INFO(RTPS_WRITER, "No reader proxy to add change.");
         check_acked_status();
     }
 ​
     // Throughput should be notified even if no matches are available
     on_publish_throughput(payload_length);
 }
 ​

主要干了这几件事

1.assert_liveliness

2.本地消息发送,跨进程消息发送

3.调用 flow_controller_的add_new_sample函数,立即发送

m_pushMode 这个默认是true,false就是表示通过heartbeat发送心跳,然后reader通过acknack确认收到哪些消息,同时需要哪些消息

reader是不是reliable,就是是不是可靠传输的reader,这个表示,如果reader某个消息丢失,reader会向writer再次请求这个消息。

reader是不是本地的reader

如果这3个条件满足一个,同时reader能够接收这个change,就直接发送,否则存入history中

4.存入history中,使用heartbeat,通知reader来获取

5.disable_positive_acks_ 如果采用disable_positive_acks_ ,需要 ack_event_ 来自动确认history中消息的ack状态

立即发出当然需要有匹配的reader才行。完成EDP匹配后,再创建writer,那么可以直接发送,如果没有完成EDP匹配,先创建writer,那么就错过了直接发送,只能通过heartbeat 和 acknack,交互之后再发送了。

剩下的 flow_controller_的add_new_sample函数,这块内容可以参考第10篇车载消息中间件FastDDS 源码解析(十)发送第一条PDP消息(上),第11篇FastDDS 源码解析(十一)发送第一条PDP消息(中)相关内容

3.2 EDP消息模型

这块内容是对19篇内容的补充。我们在创建用户自己的Writer 和 Reader之后,就会通过EDP节点,将Writer和reader的信息通过EDP节点发送出去,这样用户自己的Writer和Reader之间就建立了连接,用户自己的Writer就能将消息发送给对端的Reader。

我们在上面的源码部分可以看到,完成EDP匹配后,再创建writer,那么可以直接发送,如果没有完成EDP匹配,先创建writer,那么就错过了直接发送,只能通过heartbeat 和 acknack,交互之后再发送了。

那么我们现在抓到的信息就是没有完成EDP匹配,先创建writer。

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

image-20240124153844456.png

sequenceDiagram
participant ParticipantA		
		participant ParticipantB
			
		ParticipantA ->> ParticipantB: 1.ParticipantA发送PDP组播消息(序号71的消息)
		ParticipantB ->> ParticipantA: 2.ParticipantB收到ParticipantA发送的PDP消息,马上发送一条PDP消息(序号78的消息)
		ParticipantB ->> ParticipantA: 3.ParticipantB收到ParticipantA发送的PDP消息,匹配EDP后,马上发送3条EDP heartbeat消息(序号79,80,81的消息)
		ParticipantA ->> ParticipantB: 4.ParticipantA收到ParticipantB发送的PDP消息后,马上发送PDP消息(序号85的单播消息,87的组播消息)
		ParticipantA ->> ParticipantB: 5.ParticipantA收到ParticipantB发送的PDP消息,匹配EDP后,马上发送3条EDP heartbeat消息(序号91,94,97的消息)
		ParticipantA ->> ParticipantB: 6.ParticipantA收到ParticipantB发送的EDP heartbeat消息后,马上发送acknack消息(序号100,103,107的消息)
		ParticipantB ->> ParticipantA: 7.ParticipantB收到ParticipantA发送的EDP heartbeat消息后,马上发送acknack消息(序号109,110,116的消息)
		ParticipantB ->> ParticipantA: 8.ParticipantB收到ParticipantA发送的acknack消息后,马上发送EDP消息(序号111的消息)
    ParticipantA ->> ParticipantB: 9.ParticipantA收到ParticipantB发送的acknack消息后,马上发送EDP消息(序号114的消息)

1-7可以参考第19篇FastDDS 源码解析(十九)EDP阶段处理heartbeat消息,发送acknack消息

8.ParticipantB收到ParticipantA发送的acknack消息(就是告诉ParticipantB,这边还有消息没有收到)后,马上发送EDP消息(序号111的消息)将用户自己Writer的信息发送出来。

如图所示:这条消息主要是SEDPPubWriter发送给SEDPPubReader的消息,

image-20241213112319236.png

具体内容如下

PID_UNICAST_LOCATOR:这是这个writer的ip和端口号,我们留意一下这个端口号:7411 之前的PDP,EDP消息都是7410端口号,但是这个用户的Writer的端口号是一个7411

PID_TOPIC_NAME:Topic:Clipboard 这个item是说明这个TOPICNAME是Clipboard

PID_ENDPOINT_GUID:这个表明的是这个ENDPOINT的GUID,这是个Application-defined writer

PID_DURABILITY:TRANSIENT_LOCAL_DURABILITY_QOS 这个可以和我们之前介绍的部分对应上

PID_RELIABILITY:RELIABLE_RELIABILITY_QOS 这个表明这个writer是可靠的writer

还有一些其他的属性,不一一列举了。

截屏2024-12-12 19.50.20.png

第一篇 一个例子

车载消息中间件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(上)