第一篇 一个例子
车载消息中间件FastDDS 源码解析(一)FastDDS 介绍和使用
第二篇fastdds的组成
车载消息中间件FastDDS 源码解析(二)RtpsParticipant的创建(上)
车载消息中间件FastDDS 源码解析(三)RtpsParticipant的创建(中)
车载消息中间件FastDDS 源码解析(四)RtpsParticipant的创建(下)
车载消息中间件FastDDS 源码解析(五)BuiltinProtocols(上)
车载消息中间件FastDDS 源码解析(六)BuiltinProtocols(中)EDP
车载消息中间件FastDDS 源码解析(七)BuiltinProtocols(下)WLP&TypeLookupManager
车载消息中间件FastDDS 源码解析(八)TimedEvent
第三篇组网建立连接
pdp建连
车载消息中间件FastDDS 源码解析(十)发送第一条PDP消息(上)
FastDDS 源码解析(十二)发送第一条PDP消息(下)---异步发送
FastDDS 源码解析(十三)发送第一条PDP消息---跨进程发送
FastDDS 源码解析(十六)处理PDP消息——PDP匹配
EDP建连
FastDDS 源码解析(十七)处理PDP消息——EDP匹配
FastDDS 源码解析(十八)EDP阶段发送心跳heartbeat
FastDDS 源码解析(十九)EDP阶段处理heartbeat消息,发送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进行匹配
我们可以看到每个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消息
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的消息,
具体内容如下
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
还有一些其他的属性,不一一列举了。
第一篇 一个例子
车载消息中间件FastDDS 源码解析(一)FastDDS 介绍和使用
第二篇fastdds的组成
车载消息中间件FastDDS 源码解析(二)RtpsParticipant的创建(上)
车载消息中间件FastDDS 源码解析(三)RtpsParticipant的创建(中)
车载消息中间件FastDDS 源码解析(四)RtpsParticipant的创建(下)
车载消息中间件FastDDS 源码解析(五)BuiltinProtocols(上)
车载消息中间件FastDDS 源码解析(六)BuiltinProtocols(中)EDP
车载消息中间件FastDDS 源码解析(七)BuiltinProtocols(下)WLP&TypeLookupManager
车载消息中间件FastDDS 源码解析(八)TimedEvent
第三篇组网建立连接
pdp建连
车载消息中间件FastDDS 源码解析(十)发送第一条PDP消息(上)
FastDDS 源码解析(十二)发送第一条PDP消息(下)---异步发送
FastDDS 源码解析(十三)发送第一条PDP消息---跨进程发送
FastDDS 源码解析(十六)处理PDP消息——PDP匹配
EDP建连
FastDDS 源码解析(十七)处理PDP消息——EDP匹配
FastDDS 源码解析(十八)EDP阶段发送心跳heartbeat