第一篇 一个例子
车载消息中间件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(上)
FastDDS 源码解析(二十二)创建User的DataWriter(下)
FastDDS 源码解析(二十三)创建User的DataReader(上)
2.注册DataReader
这一篇我们介绍一下注册DataReader,为什么需要注册这个DataReader,这个注册是向EDP注册一下,这个DataReader的信息。
EDP会把这个信息发送给对端的EDP,这样对端就会知道有这个DataReader,如果远端有可以匹配的DataWriter,那么这两个
DataWriter 和 DataReader可以互相发送消息,发送heartbeat 和acknack消息,也可以发送消息。
这一篇就是介绍一下这个过程
2.1时序图
sequenceDiagram
participant DataReaderImpl
participant RTPSParticipantImpl
participant BuiltinProtocols
Participant EDP
Participant PDP
DataReaderImpl ->> RTPSParticipantImpl:1.registerReader
RTPSParticipantImpl ->> BuiltinProtocols:2.addLocalReader
BuiltinProtocols ->> EDP:3.newLocalReaderProxyData
EDP ->> PDP:4.addReaderProxyData
EDP ->> EDP:5.pairing_reader_proxy_with_any_local_writer
EDP ->> EDP:6.pairingReader
EDP ->> EDPSimple:7.processLocalReaderProxyData
BuiltinProtocols ->> WLP:8.add_local_reader
1.RTPSParticipant的registerWriter函数 调用了RTPSParticipantImpl的registerWriter来注册这个writer
RTPSParticipantImpl的registerWriter调用了BuiltinProtocols的addLocalWriter
2.BuiltinProtocols的addLocalReader主要干了2件事
a.BuiltinProtocols的newLocalReaderProxyData 见步骤3
b.调用WLP的add_local_reader。见步骤8:
3.BuiltinProtocols的newLocalWriterReaderData主要干了4件事
a.调用 PDP::addReaderProxyData 见步骤4
这儿有一个lamanda函数 init_fun,这个作为参数传递给addReaderProxyData
b.调用 pairing_reader_proxy_with_any_local_writer,与本地的writer进行匹配 见步骤5
c.调用 pairingReader 与已经知道的远端的Writer 匹配 见步骤6
d.调用 processLocalReaderProxyData 向远端的Reader发送消息 见步骤7
4.PDP的addReaderProxyData
先查找participant_proxies_,找到这个writer对应的ParticipantProxyData,在这个ParticipantProxyData中查找对应的ReaderProxyData,找到就更新ReaderProxyData
没有找到就新建一个ReaderProxyData,将ReaderProxyData信息存入ParticipantProxyData
5.查看本地的UserWriter,看一下Writer 和这个新建的reader是否匹配(reader和writer的很多qos是否一致)
如果匹配将reader加入到writer匹配的reader的队列中。这个是本地writer的匹配
6.遍历了participant_proxies_,找到其中的writer,与我们的reader进行匹配。
如果匹配将reader加入到writer匹配的reader的队列中。这个主要是远端writer的匹配。
7.主要是将reader 的信息,序列化后发送给远端
8.WLP来管理我们这个reader的liveliness信息
2.2源码
步骤1:
bool RTPSParticipant::registerReader(
RTPSReader* Reader,
const TopicAttributes& topicAtt,
const ReaderQos& rqos,
const fastdds::rtps::ContentFilterProperty* content_filter)
{
return mp_impl->registerReader(Reader, topicAtt, rqos, content_filter);
}
调用RTPSParticipantImpl的registerReader函数
bool RTPSParticipantImpl::registerReader(
RTPSReader* reader,
const TopicAttributes& topicAtt,
const ReaderQos& rqos,
const fastdds::rtps::ContentFilterProperty* content_filter)
{
return this->mp_builtinProtocols->addLocalReader(reader, topicAtt, rqos, content_filter);
}
步骤2:
bool BuiltinProtocols::addLocalReader(
RTPSReader* R,
const fastrtps::TopicAttributes& topicAtt,
const fastrtps::ReaderQos& rqos,
const fastdds::rtps::ContentFilterProperty* content_filter)
{
bool ok = true;
if (mp_PDP != nullptr)
{
ok = mp_PDP->getEDP()->newLocalReaderProxyData(R, topicAtt, rqos, content_filter);
if (!ok)
{
EPROSIMA_LOG_WARNING(RTPS_EDP, "Failed register ReaderProxyData in EDP");
return false;
}
}
else
{
EPROSIMA_LOG_WARNING(RTPS_EDP, "EDP is not used in this Participant, register a Reader is impossible");
}
if (mp_WLP != nullptr)
{
ok &= mp_WLP->add_local_reader(R, rqos);
}
return ok;
}
主要干了2件事:
1.BuiltinProtocols的newLocalReaderProxyData 见步骤3
2.调用WLP的add_local_reader。见步骤8:
步骤3:
bool EDP::newLocalReaderProxyData(
RTPSReader* reader,
const TopicAttributes& att,
const ReaderQos& rqos,
const fastdds::rtps::ContentFilterProperty* content_filter)
{
EPROSIMA_LOG_INFO(RTPS_EDP, "Adding " << reader->getGuid().entityId << " in topic " << att.topicName);
auto init_fun = [this, reader, &att, &rqos, content_filter](
ReaderProxyData* rpd,
bool updating,
const ParticipantProxyData& participant_data)
{
if (updating)
{
EPROSIMA_LOG_ERROR(RTPS_EDP,
"Adding already existent reader " << reader->getGuid().entityId << " in topic "
<< att.topicName);
return false;
}
const NetworkFactory& network = mp_RTPSParticipant->network_factory();
const auto& ratt = reader->getAttributes();
rpd->isAlive(true);
rpd->m_expectsInlineQos = reader->expectsInlineQos();
rpd->guid(reader->getGuid());
rpd->key() = rpd->guid();
if (ratt.multicastLocatorList.empty() && ratt.unicastLocatorList.empty())
{
rpd->set_locators(participant_data.default_locators);
}
else
{
rpd->set_multicast_locators(ratt.multicastLocatorList, network);
rpd->set_announced_unicast_locators(ratt.unicastLocatorList);
fastdds::rtps::ExternalLocatorsProcessor::add_external_locators(*rpd,
ratt.external_unicast_locators);
}
rpd->RTPSParticipantKey() = mp_RTPSParticipant->getGuid();
rpd->topicName(att.getTopicName());
rpd->typeName(att.getTopicDataType());
rpd->topicKind(att.getTopicKind());
if (att.type_id.m_type_identifier._d() != static_cast<uint8_t>(0x00))
{
rpd->type_id(att.type_id);
}
if (att.type.m_type_object._d() != static_cast<uint8_t>(0x00))
{
rpd->type(att.type);
}
if (att.type_information.assigned())
{
rpd->type_information(att.type_information);
}
rpd->m_qos.setQos(rqos, true);
rpd->userDefinedId(ratt.getUserDefinedID());
if (nullptr != content_filter)
{
// Check content of ContentFilterProperty.
if (!(0 < content_filter->content_filtered_topic_name.size() &&
0 < content_filter->related_topic_name.size() &&
0 < content_filter->filter_class_name.size() &&
0 < content_filter->filter_expression.size()
))
{
return false;
}
rpd->content_filter(*content_filter);
}
#if HAVE_SECURITY
if (mp_RTPSParticipant->is_secure())
{
rpd->security_attributes_ = ratt.security_attributes().mask();
rpd->plugin_security_attributes_ = ratt.security_attributes().plugin_endpoint_attributes;
}
else
{
rpd->security_attributes_ = 0UL;
rpd->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(rpd->typeName().c_str());
if (type_info != nullptr)
{
rpd->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(
rpd->typeName().c_str());
if (type_id != nullptr)
{
has_type_id = true;
rpd->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 &&
rpd->type_id().m_type_identifier._d() == types::EK_COMPLETE;
const types::TypeObject* type_obj =
types::TypeObjectFactory::get_instance()->get_type_object(
rpd->typeName().c_str(), type_is_complete);
if (type_obj != nullptr)
{
rpd->type().m_type_object = *type_obj;
}
}
}
return true;
};
//ADD IT TO THE LIST OF READERPROXYDATA
GUID_t participant_guid;
ReaderProxyData* reader_data = this->mp_PDP->addReaderProxyData(reader->getGuid(), participant_guid, init_fun);
if (reader_data == nullptr)
{
return false;
}
//PAIRING
if (this->mp_PDP->getRTPSParticipant()->should_match_local_endpoints())
{
pairing_reader_proxy_with_any_local_writer(participant_guid, reader_data);
}
pairingReader(reader, participant_guid, *reader_data);
//DO SOME PROCESSING DEPENDING ON THE IMPLEMENTATION (SIMPLE OR STATIC)
processLocalReaderProxyData(reader, reader_data);
return true;
}
主要干了4件事
1.调用 PDP::addReaderProxyData
2.调用 pairing_reader_proxy_with_any_local_writer,与本地的writer进行匹配
3.调用 pairingReader 与已经知道的远端的Writer 匹配
4.调用 processLocalWriterProxyData 向远端的Writer发送消息
步骤4:
ReaderProxyData* PDP::addReaderProxyData(
const GUID_t& reader_guid,
GUID_t& participant_guid,
std::function<bool(ReaderProxyData*, bool, const ParticipantProxyData&)> initializer_func)
{
EPROSIMA_LOG_INFO(RTPS_PDP, "Adding reader proxy data " << reader_guid);
ReaderProxyData* ret_val = nullptr;
// notify statistics module
getRTPSParticipant()->on_entity_discovery(reader_guid, ParameterPropertyList_t());
std::lock_guard<std::recursive_mutex> guardPDP(*this->mp_mutex);
for (ParticipantProxyData* pit : participant_proxies_)
{
if (pit->m_guid.guidPrefix == reader_guid.guidPrefix)
{
// Copy participant data to be used outside.
participant_guid = pit->m_guid;
// Check that it is not already there:
auto rpi = pit->m_readers->find(reader_guid.entityId);
if ( rpi != pit->m_readers->end())
{
ret_val = rpi->second;
if (!initializer_func(ret_val, true, *pit))
{
return nullptr;
}
RTPSParticipantListener* listener = mp_RTPSParticipant->getListener();
if (listener)
{
ReaderDiscoveryInfo info(*ret_val);
info.status = ReaderDiscoveryInfo::CHANGED_QOS_READER;
listener->onReaderDiscovery(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 (reader_proxies_pool_.empty())
{
size_t max_proxies = reader_proxies_pool_.max_size();
if (reader_proxies_number_ < max_proxies)
{
// Pool is empty but limit has not been reached, so we create a new entry.
++reader_proxies_number_;
ret_val = new ReaderProxyData(
mp_RTPSParticipant->getAttributes().allocation.locators.max_unicast_locators,
mp_RTPSParticipant->getAttributes().allocation.locators.max_multicast_locators,
mp_RTPSParticipant->getAttributes().allocation.data_limits,
mp_RTPSParticipant->getAttributes().allocation.content_filter);
}
else
{
EPROSIMA_LOG_WARNING(RTPS_PDP, "Maximum number of reader proxies (" << max_proxies <<
") reached for participant " << mp_RTPSParticipant->getGuid() << std::endl);
return nullptr;
}
}
else
{
// Pool is not empty, use entry from pool
ret_val = reader_proxies_pool_.back();
reader_proxies_pool_.pop_back();
}
// Add to ParticipantProxyData
(*pit->m_readers)[reader_guid.entityId] = ret_val;
if (!initializer_func(ret_val, false, *pit))
{
return nullptr;
}
RTPSParticipantListener* listener = mp_RTPSParticipant->getListener();
if (listener)
{
ReaderDiscoveryInfo info(*ret_val);
info.status = ReaderDiscoveryInfo::DISCOVERED_READER;
listener->onReaderDiscovery(mp_RTPSParticipant->getUserRTPSParticipant(), std::move(info));
check_and_notify_type_discovery(listener, *ret_val);
}
return ret_val;
}
}
return nullptr;
}
这个函数主要是为了创建ReaderProxyData的对象,配置参数等
先查找participant_proxies_,找到这个Reader对应的ParticipantProxyData,在这个ParticipantProxyData中查找对应的ReaderProxyData,找到就更新ReaderProxyData
没有找到就新建一个ReaderProxyData,将ReaderProxyData信息存入ParticipantProxyData
步骤5:
bool EDP::pairing_reader_proxy_with_any_local_writer(
const GUID_t& participant_guid,
ReaderProxyData* rdata)
{
(void)participant_guid;
EPROSIMA_LOG_INFO(RTPS_EDP, rdata->guid() << " in topic: "" << rdata->topicName() << """);
mp_RTPSParticipant->forEachUserWriter([&, rdata](RTPSWriter& w) -> bool
{
auto temp_writer_proxy_data = get_temporary_writer_proxies_pool().get();
GUID_t writerGUID = w.getGuid();
if (mp_PDP->lookupWriterProxyData(writerGUID, *temp_writer_proxy_data))
{
MatchingFailureMask no_match_reason;
fastdds::dds::PolicyMask incompatible_qos;
bool valid = valid_matching(temp_writer_proxy_data.get(), rdata, no_match_reason, incompatible_qos);
const GUID_t& reader_guid = rdata->guid();
temp_writer_proxy_data.reset();
if (valid)
{
#if HAVE_SECURITY
if (!mp_RTPSParticipant->security_manager().discovered_reader(writerGUID, participant_guid,
*rdata, w.getAttributes().security_attributes()))
{
EPROSIMA_LOG_ERROR(RTPS_EDP, "Security manager returns an error for writer " << writerGUID);
}
#else
if (w.matched_reader_add(*rdata))
{
EPROSIMA_LOG_INFO(RTPS_EDP_MATCH,
"RP:" << rdata->guid() << " match W:" << w.getGuid() << ". RLoc:" <<
rdata->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 PublicationMatchedStatus& pub_info =
update_publication_matched_status(reader_guid, writerGUID, 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);
}
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 PublicationMatchedStatus& pub_info =
update_publication_matched_status(reader_guid, writerGUID, -1);
w.getListener()->onWriterMatched(&w, pub_info);
}
}
}
}
// next iteration
return true;
});
return true;
}
查看本地的UserWriter,调用valid_matching 看一下Writer 和这个新建的reader是否匹配(reader和writer的很多qos是否一致)
如果匹配matched_reader_add函数将reader加入到writer匹配的reader的队列中,(这块可以参考FastDDS 源码解析(十六)处理PDP消息——PDP匹配)。
步骤6:
bool EDP::pairingReader(
RTPSReader* R,
const GUID_t& participant_guid,
const ReaderProxyData& rdata)
{
(void)participant_guid;
EPROSIMA_LOG_INFO(RTPS_EDP, rdata.guid() << " in topic: "" << rdata.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++;
}
for (; pit != mp_PDP->ParticipantProxiesEnd(); ++pit)
{
for (auto& pair : *(*pit)->m_writers)
{
WriterProxyData* wdatait = pair.second;
MatchingFailureMask no_match_reason;
fastdds::dds::PolicyMask incompatible_qos;
bool valid = valid_matching(&rdata, wdatait, no_match_reason, incompatible_qos);
const GUID_t& reader_guid = R->getGuid();
const GUID_t& writer_guid = wdatait->guid();
if (valid)
{
#if HAVE_SECURITY
if (!mp_RTPSParticipant->security_manager().discovered_writer(R->m_guid, (*pit)->m_guid,
*wdatait, R->getAttributes().security_attributes()))
{
EPROSIMA_LOG_ERROR(RTPS_EDP, "Security manager returns an error for reader " << reader_guid);
}
#else
if (R->matched_writer_add(*wdatait))
{
EPROSIMA_LOG_INFO(RTPS_EDP_MATCH,
"WP:" << wdatait->guid() << " match R:" << R->getGuid() << ". RLoc:" <<
wdatait->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(reader_guid, 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);
}
//EPROSIMA_LOG_INFO(RTPS_EDP,RTPS_CYAN<<"Valid Matching to writerProxy: "<<wdatait->m_guid<<RTPS_DEF<<endl);
if (R->matched_writer_is_matched(wdatait->guid())
&& R->matched_writer_remove(wdatait->guid()))
{
#if HAVE_SECURITY
mp_RTPSParticipant->security_manager().remove_writer(reader_guid, participant_guid,
wdatait->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(reader_guid, writer_guid, -1);
R->getListener()->onReaderMatched(R, sub_info);
}
}
}
}
}
return true;
}
这个函数主要从匹配的participant 中寻找writer,与reader进行匹配
我们可以看到每个pdp中都会把匹配的Participant的信息,存入到participant_proxies_ ,本地的participant的信息,作为第一个ParticipantProxyData,存入到 participant_proxies_ ,远程的Participant的信息,也会存入到 participant_proxies_中去。
这个函数遍历了participant_proxies_,找到其中的writer,与我们的reader进行匹配。
如果匹配将reader加入到writer匹配的reader的队列中。
步骤7:
bool EDPSimple::processLocalReaderProxyData(
RTPSReader* local_reader,
ReaderProxyData* rdata)
{
EPROSIMA_LOG_INFO(RTPS_EDP, rdata->guid().entityId);
(void)local_reader;
auto* writer = &subscriptions_writer_;
#if HAVE_SECURITY
if (local_reader->getAttributes().security_attributes().is_discovery_protected)
{
writer = &subscriptions_secure_writer_;
}
#endif // if HAVE_SECURITY
CacheChange_t* change = nullptr;
bool ret_val = serialize_reader_proxy_data(*rdata, *writer, true, &change);
if (change != nullptr)
{
writer->second->add_change(change);
}
return ret_val;
}
这个函数主要是将reader 的信息,序列化后发送给远端
subscriptions_writer_是发送者,信息是这个ReaderProxyData,这里面就是将ReaderProxyData中的信息,转化为一个CacheChange_t对象,然后调用WriterHistory的add_change函数
3.发送消息
3.1源码
可以参考FastDDS 源码解析(二十二)创建User的DataWriter(下)
3.2 消息模型
这块内容是对19篇内容的补充。我们在创建用户自己的Writer 和 Reader之后,就会通过EDP节点,将Writer和reader的信息通过EDP节点发送出去,这样用户自己的Writer和Reader之间就建立了连接,用户自己的Writer就能将消息发送给对端的Reader。
我们在上面的源码部分可以看到,完成EDP匹配后,再创建writer,那么可以直接发送,如果没有完成EDP匹配,先创建writer,那么就错过了直接发送,只能通过heartbeat 和 acknack,交互之后再发送了。
那么我们现在抓到的信息就是没有完成EDP匹配,先创建writer。
参考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可以参考FastDDS 源码解析(十九)EDP阶段处理heartbeat消息,发送acknack消息第19篇
8.ParticipantB收到ParticipantA发送的acknack消息(就是告诉ParticipantB,这边还有消息没有收到)后,马上发送EDP消息(序号111的消息)将用户自己Writer的信息发送出来。
9.ParticipantA收到ParticipantB发送的acknack消息(就是告诉ParticipantA,这边还有消息没有收到)后,马上发送EDP消息(序号114的消息)将用户自己Reader的信息发送出来。
如图所示:这条消息主要是SEDPSubWriter发送给SEDPSubReader的消息
具体内容如下
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 reader
PID_DURABILITY:TRANSIENT_LOCAL_DURABILITY_QOS 这个可以和我们之前介绍的部分对应上
PID_RELIABILITY:RELIABLE_RELIABILITY_QOS 这个表明这个writer是可靠的writer
还有一些其他的属性,不一一列举了。
到这儿,用户自己的writer 和 reader的信息进行了交互,writer和reader之间就能进行通信了。
第一篇 一个例子
车载消息中间件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(上)