目录:
第一篇 一个例子
车载消息中间件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消息
上一篇我们讲到了PDP的初始化,这一篇我们主要讲一下EDP的初始化。
1.EDP
1.1EDP简介
EDP 全称是Endpoint Discovery Protocol,就是端点发现协议,这个端点在这儿指的是Writer 和 Reader。在pdp阶段(participant 发现)之后,就是EDP阶段,writer 和 reader的互相发现。
在上一篇的2.4 pdp源码解析部门的PDPSimple::init 函数中我们看到
pdpsimple 初始化的时候主要干了2件事
1.pdp 初始化
2.如果use_STATIC_EndpointDiscoveryProtocol 参数为true 创建EDPStatic 对象
如果use_SIMPLE_EndpointDiscoveryProtocol 参数为true 创建EDPSimple对象
其实这就是选择 edp 的发现协议
static 发现协议 或者 simple发现协议,static 发现协议主要指的,将不走edp 发现协议,取而代之的是xml 静态配置文件。这个的好处是能跳过了EDP发现阶段。但是缺少了一定的灵活性,使用static协议,需要提前知道,datawriter ,datareader,topics,types这些信息,同时要知道这些信息的一一对应关系。
默认使用simple edp发现协议,如果要使用static发现协议需要配置参数。EDP static发现协议用的比较少,simple edp用的比较到。
下面会介绍simple edp 发现协议。
1.2EDP的Writer和Reader
EDP 可能会有
SEDPbuiltinPublicationsWriter
SEDPbuiltinPublicationsReader
SEDPbuiltinSubscriptionsWriter
SEDPbuiltinSubscriptionsReader
SEDPbuiltinTopicsWriter (可选)
SEDPbuiltinTopicsReader (可选)
这几个writer 和 reader,是在pdp 阶段之后互相建立连接,主要传输的是 participant 中的userwriter,userreader 信息,pdp 和 edp建立的writer 和 reader都是builtin ,都是内置的。
EDP 中包含的端点有4个SEDPbuiltinPublicationsWriter,SEDPbuiltinPublicationsReader,SEDPbuiltinSubscriptionsWriter,SEDPbuiltinSubscriptionsReader。
这4个端点可以分为2组,SEDPbuiltinPublicationsWriter,SEDPbuiltinPublicationsReader一组,就是表示传递应用程序的writer信息。
在user 创建完Publisher之后,这个publisher包含的UserWriter的信息,将通过SEDPbuiltinPublicationsWriter传递出去,对端的participant 的SEDPbuiltinPublicationsReader接收到消息后,与UserReader 配对。
SEDPbuiltinSubscriptionsWriter,SEDPbuiltinSubscriptionsReader一组,表示传递应用程序的reader信息。在user 创建完Subscriber之后,这个Subscriber包含的UserReader的信息,将通过SEDPbuiltinSubscriptionsWriter传递出去,对端的participant 的SEDPbuiltinSubscriptionsReader接收到消息后,与UserWriter 配对。
而userwriter 和 userreader 是应用方,是使用者(可以参看车载消息中间件FastDDS 源码解析(五)BuiltinProtocols(上) 1.2节部分 )。
1.3EDP初始化时序图
sequenceDiagram
participant PDPSimple
participant EDPSimple
participant EDPUtils
participant RTPSParticipantImpl
participant StatefulWriter
participant StatefulReader
PDPSimple ->> EDPSimple: 1.new EDPSimple
PDPSimple ->> EDPSimple: 2.initEDP
EDPSimple ->> EDPSimple: 3.createSEDPEndpoints
EDPSimple ->> EDPUtils: 4.create_edp_writer
EDPUtils ->> RTPSParticipantImpl: 5.createWriter
RTPSParticipantImpl ->> RTPSParticipantImpl: 6.create_writer
RTPSParticipantImpl ->> StatefulWriter: 7.new
EDPSimple ->> EDPUtils:8.create_edp_reader
EDPUtils ->> RTPSParticipantImpl:9.createReader
RTPSParticipantImpl ->> RTPSParticipantImpl:10.create_reader
RTPSParticipantImpl ->> StatefulReader: 11.new
1.PDPSimple 创建EDPSimple对象
2.PDPSimple 调用EDPSimple 的 initEDP 初始化EDPSimple
3.在初始化EDPSimple 的过程中,createSEDPEndpoints,就是创建EDPSimple的endpoint,也就是StatefulWriter 和StatefulReader
4.在createSEDPEndpoints 函数中调用EDPUtils 的 create_edp_writer
5.在EDPUtils 的 create_edp_writer函数中调用RTPSParticipantImpl的createWriter函数
6.RTPSParticipantImpl的createWriter函数 调用自己的create_writer函数
7.最终new了一个StatefulWriter
8,9,10,11 类似
步骤2:初始化EDPSimple
bool EDPSimple::initEDP(
BuiltinAttributes& attributes)
{
EPROSIMA_LOG_INFO(RTPS_EDP, "Beginning Simple Endpoint Discovery Protocol");
m_discovery = attributes;
if (!createSEDPEndpoints())
{
EPROSIMA_LOG_ERROR(RTPS_EDP, "Problem creation SimpleEDP endpoints");
return false;
}
#if HAVE_SECURITY
if (mp_RTPSParticipant->is_secure() && !create_sedp_secure_endpoints())
{
EPROSIMA_LOG_ERROR(RTPS_EDP, "Problem creation SimpleEDP endpoints");
return false;
}
#endif // if HAVE_SECURITY
return true;
}
初始化 主要是调用 createSEDPEndpoints,来创建 statefulreader 和 statefulwriter
创建reader 和 writer 在PDP初始化中有介绍
步骤3创建SEDPEndpoints(Writer 和 Reader):
bool EDPSimple::createSEDPEndpoints()
{
WriterAttributes watt;
ReaderAttributes ratt;
HistoryAttributes reader_history_att;
HistoryAttributes writer_history_att;
set_builtin_reader_history_attributes(reader_history_att);
set_builtin_writer_history_attributes(writer_history_att);
set_builtin_reader_attributes(ratt);
set_builtin_writer_attributes(watt);
publications_listener_ = new EDPSimplePUBListener(this);
subscriptions_listener_ = new EDPSimpleSUBListener(this);
if (m_discovery.discovery_config.m_simpleEDP.use_PublicationWriterANDSubscriptionReader)
{
if (!EDPUtils::create_edp_writer(mp_RTPSParticipant, "DCPSPublications", c_EntityId_SEDPPubWriter,
writer_history_att, watt, publications_listener_, pub_writer_payload_pool_, publications_writer_))
{
return false;
}
EPROSIMA_LOG_INFO(RTPS_EDP, "SEDP Publication Writer created");
if (!EDPUtils::create_edp_reader(mp_RTPSParticipant, "DCPSSubscriptions", c_EntityId_SEDPSubReader,
reader_history_att, ratt, subscriptions_listener_, sub_reader_payload_pool_, subscriptions_reader_))
{
return false;
}
EPROSIMA_LOG_INFO(RTPS_EDP, "SEDP Subscription Reader created");
}
if (m_discovery.discovery_config.m_simpleEDP.use_PublicationReaderANDSubscriptionWriter)
{
if (!EDPUtils::create_edp_reader(mp_RTPSParticipant, "DCPSPublications", c_EntityId_SEDPPubReader,
reader_history_att, ratt, publications_listener_, pub_reader_payload_pool_, publications_reader_))
{
return false;
}
EPROSIMA_LOG_INFO(RTPS_EDP, "SEDP Publication Reader created");
if (!EDPUtils::create_edp_writer(mp_RTPSParticipant, "DCPSSubscriptions", c_EntityId_SEDPSubWriter,
writer_history_att, watt, subscriptions_listener_, sub_writer_payload_pool_, subscriptions_writer_))
{
return false;
}
EPROSIMA_LOG_INFO(RTPS_EDP, "SEDP Subscription Writer created");
}
EPROSIMA_LOG_INFO(RTPS_EDP, "Creation finished");
return true;
}
这里面有两个参数比较关键use_PublicationWriterANDSubscriptionReader,use_PublicationReaderANDSubscriptionWriter
use_PublicationWriterANDSubscriptionReader为true 并且use_PublicationReaderANDSubscriptionWriter为false:表示本地只是实现PublicationWriter 和 SubscriptionReader
什么意思哪,本地participant只有Publisher,只有user 的writer,大概结构入下图所示,左侧表示本地的participant
use_PublicationWriterANDSubscriptionReader为false并且use_PublicationReaderANDSubscriptionWriter为true:这个参数表示本地只是实现PublicationReader 和 SubscriptionWriter
什么意思哪,本地participant只有Subscriber,也就是只有userReader 如下图所示,左侧表示本地的participant
两个参数都为true,表示本地edp这几个内置的writer,reader 都会实现。
步骤三代码逻辑,是会创建writer 和 reader,创建顺序是有可能先创建writer,再创建reader,也有可能先创建reader再创建writer,
我们在这儿先看writer 的创建。
创建EDP的Writer(StatefulWriter)
步骤4:
static bool create_edp_writer(
RTPSParticipantImpl* participant,
const std::string& topic_name,
const EntityId_t& entity_id,
const HistoryAttributes& history_att,
WriterAttributes& watt,
WriterListener* listener,
std::shared_ptr<ITopicPayloadPool>& payload_pool,
WriterHistoryPair& edp_writer)
{
RTPSWriter* waux = nullptr;
payload_pool = create_payload_pool(topic_name, history_att, false);
edp_writer.second = new WriterHistory(history_att);
bool created =
participant->createWriter(&waux, watt, payload_pool, edp_writer.second, listener, entity_id,
true);
if (created)
{
edp_writer.first = dynamic_cast<StatefulWriter*>(waux);
}
else
{
delete(edp_writer.second);
edp_writer.second = nullptr;
release_payload_pool(payload_pool, history_att, false);
}
return created;
}
//这个函数主要是创建writer,先创建一个WriterHistory,里面有一个缓存来存储消息,每一个writer要发送的消息都先放入这个缓存中,fastdds 会配置一个机制来管理缓存。
相对应的reader也有一个缓存,writer的消息,先通过网络发送给对端的participant,然后再存入reader的缓存。然后再交给上层处理。
这里面涉及到一个qos
HISTORY_QOS_POLICY
这个qos有两个配置选项:
KEEP_LAST 只保存最近的一个,这个缓存只有一个,那么多余一个的消息同时到来,会导致丢包,那么KEEP_LAST就和之前介绍的一个qos :ReliabilityQos中besteffort差不多了,都会丢包。
KEEP_ALL 保存所有的消息,这个其实还需要设置缓存大小,当缓存满了(所有的消息还没被处理)的时候,也会丢消息。
这个qos 需要 和 ReliabilityQos 配合使用同时还需要配置 缓存大小。
我们看到想要让 fast dds 按照某种规则来运行很多时候都需要几个qos 配合使用的。
每个qos 有不同的配置项,组合使用几个qos使得fast dds按照我们设计的规则运行。
不同的qos运行在writer 和 reader上,需要配合使用,举个例子,当writer 的ReliabilityQos 设置成besteffort,如果reader设置成reliable那也是没效果的。
有些qos 可以不管对端的qos设置,也能起效,但是更多的qos 需要本地和对端配合使用。
步骤5:
bool RTPSParticipantImpl::createWriter(
RTPSWriter** WriterOut,
WriterAttributes& param,
const std::shared_ptr<IPayloadPool>& payload_pool,
WriterHistory* hist,
WriterListener* listen,
const EntityId_t& entityId,
bool isBuiltin)
{
if (!payload_pool)
{
EPROSIMA_LOG_ERROR(RTPS_PARTICIPANT, "Trying to create writer with null payload pool");
return false;
}
auto callback = [hist, listen, &payload_pool, this]
(const GUID_t& guid, WriterAttributes& param, fastdds::rtps::FlowController* flow_controller,
IPersistenceService* persistence, bool is_reliable) -> RTPSWriter*
{
if (is_reliable)
{
if (persistence != nullptr)
{
return new StatefulPersistentWriter(this, guid, param, payload_pool, flow_controller,
hist, listen, persistence);
}
else
{
return new StatefulWriter(this, guid, param, payload_pool, flow_controller,
hist, listen);
}
}
else
{
if (persistence != nullptr)
{
return new StatelessPersistentWriter(this, guid, param, payload_pool, flow_controller,
hist, listen, persistence);
}
else
{
return new StatelessWriter(this, guid, param, payload_pool, flow_controller,
hist, listen);
}
}
};
return create_writer(WriterOut, param, entityId, isBuiltin, callback);
}
这个函数在介绍PDP初始化的时候介绍过,在这儿主要是创建了一个回调函数,回调函数后续被调用到用来创建StatefulWriter
步骤6:
bool RTPSParticipantImpl::create_writer(
RTPSWriter** writer_out,
WriterAttributes& param,
const EntityId_t& entity_id,
bool is_builtin,
const Functor& callback)
{
······
//省略部分代码
······
RTPSWriter* SWriter = nullptr;
SWriter = callback(guid, param, flow_controller, persistence, param.endpoint.reliabilityKind == RELIABLE);
······
//省略部分代码
······
createSendResources(SWriter);
if (param.endpoint.reliabilityKind == RELIABLE)
{
if (!createAndAssociateReceiverswithEndpoint(SWriter))
{
delete(SWriter);
return false;
}
}
······
//省略部分代码
······
return true;
}
1.调用FlowControllerFactory::retrieve_flow_controller,创建或者获取一个flowcontroller
我们在之前讲过flowcontroller的相关内容,flowcontroller主要是控制发送策略。
先根据传入的参数WriterAttributes,来创建或者获取一个flowcontroller。
如果没有成功,再根据 RTPSParticipantImpl的参数 来创建或者获取一个flowcontroller,如果没有成功,创建或者获取一个默认的flowcontroller。
每一个RTPSWriter 都会有一个flow_controller与之匹配,一个flow_controller 可能有多个RTPSWriter与之关联
2.以flow_controller为参数调用回调函数来创建writer,将writer放入m_allWriterList中去,如果不是内置的writer,则放入 m_userWriterList中去
3.创建senderresource
4.如果是Statefulwriter,调用createAndAssociateReceiverswithEndpoint
创建ReceiverResources,将ReceiverResources的messageReceiver 与RTPSReader关联
在这里创建的是statelesswriter ,那么不会创建receiverresource。
这边1,2,3 可以参看PDP writer的创建,是一样的。
我们看一下4
只有当这个writer 是 RELIABLE的时候才会调用createAndAssociateReceiverswithEndpoint,statefulwriter 是 RELIABLE的,所以会调用到。createAndAssociateReceiverswithEndpoint看字面意思就是创建receiverresource,并将receiver和Endpoint 关联。
receiverresource(也就是接收消息的socket)接到消息之后,将会让和receiverresource相关的messagereceiver处理消息,messagerreceiver做消息解析之后,找到接收者,也就是endpoint(rtpsreader或者rtpswriter),这些endpoint会做处理。
如果writer 是 RELIABLE,那么需要创建receiverresource,这个主要是为了收消息,对端有收到消息会发一个确认消息,如果没有收到消息,需要重传,那么收到消息就需要处理消息。。
创建ReceiverResources,将ReceiverResources的messageReceiver 与RTPSReader关联
bool RTPSParticipantImpl::createAndAssociateReceiverswithEndpoint(
Endpoint* pend,
bool unique_flows,
uint16_t initial_unique_port,
uint16_t final_unique_port)
{
/*This function...
- Asks the network factory for new resources
- Encapsulates the new resources within the ReceiverControlBlock list
- Associated the endpoint to the new elements in the list
- Launches the listener thread
*/
auto& attributes = pend->getAttributes();
// unique_flows 是不是这个endpoint 独立的port号
// 如果是单独的port,从initial_unique_port 到 final_unique_port 尝试创建 接收socket
// 一般不会配置,所以基本会走else 分支
if (unique_flows)
{
attributes.multicastLocatorList.clear();
attributes.unicastLocatorList = m_att.defaultUnicastLocatorList;
attributes.external_unicast_locators.clear();
uint16_t port = initial_unique_port;
while (port < final_unique_port)
{
// Set port on unicast locators
for (Locator_t& loc : attributes.unicastLocatorList)
{
loc.port = port;
}
// Try creating receiver resources
if (createReceiverResources(attributes.unicastLocatorList, false, true))
{
break;
}
// Try with next port
++port;
}
// Fail when unique ports are exhausted
if (port >= final_unique_port)
{
EPROSIMA_LOG_ERROR(RTPS_PARTICIPANT, "Unique flows requested but exhausted. Port range: "
<< initial_unique_port << "-" << final_unique_port);
return false;
}
}
else
{
// 1 - Ask the network factory to generate the elements that do still not exist
//Iterate through the list of unicast and multicast locators the endpoint has... unless its empty
//In that case, just use the standard
if (attributes.unicastLocatorList.empty() && attributes.multicastLocatorList.empty())
{
// Take default locators from the participant.
attributes.unicastLocatorList = m_att.defaultUnicastLocatorList;
attributes.multicastLocatorList = m_att.defaultMulticastLocatorList;
attributes.external_unicast_locators = m_att.default_external_unicast_locators;
}
// 创建ReceiverResources,设置标志位开始接收message,true表示receiverresource regisetreceiver上 messagereceiver
createReceiverResources(attributes.unicastLocatorList, false, true);
createReceiverResources(attributes.multicastLocatorList, false, true);
}
// 将unicastLocatorList 加到external_unicast_locators 中去
fastdds::rtps::ExternalLocatorsProcessor::set_listening_locators(attributes.external_unicast_locators,
attributes.unicastLocatorList);
// Associate the Endpoint with ReceiverControlBlock
// 将messageReceiver 与endpoint关联
assignEndpointListenResources(pend);
return true;
}
主要干了2件事:
1.createReceiverResources 创建ReceiverResources 这部分内容可以看一下,participant 初始化部分。
2.assignEndpointListenResources 将messageReceiver 与endpoint关联
首先查看edp的unicastLocatorList 和multicastLocatorList是否为空,为空使用participant的unicastLocatorList 和multicastLocatorList
bool RTPSParticipantImpl::assignEndpointListenResources(
Endpoint* endp)
{
//Tag the endpoint with the ReceiverResources
bool valid = true;
/* No need to check for emptiness on the lists, as it was already done on part function
In case are using the default list of Locators they have already been embedded to the parameters
*/
//UNICAST
// 将 messageReceiver 与endp关联
assignEndpoint2LocatorList(endp, endp->getAttributes().unicastLocatorList);
//MULTICAST
assignEndpoint2LocatorList(endp, endp->getAttributes().multicastLocatorList);
return valid;
}
这儿调用到了assignEndpoint2LocatorList
bool RTPSParticipantImpl::assignEndpoint2LocatorList(
Endpoint* endp,
LocatorList_t& list)
{
/* Note:
The previous version of this function associated (or created) ListenResources and added the endpoint to them.
It then requested the list of Locators the Listener is listening to and appended to the LocatorList_t from the parameters.
This has been removed because it is considered redundant. For ReceiveResources that listen on multiple interfaces, only
one of the supported Locators is needed to make the match, and the case of new ListenResources being created has been removed
since its the NetworkFactory the one that takes care of Resource creation.
*/
for (auto lit = list.begin(); lit != list.end(); ++lit)
{
//Iteration of all Locators within the Locator list passed down as argument
std::lock_guard<std::mutex> guard(m_receiverResourcelistMutex);
//Check among ReceiverResources whether the locator is supported or not
for (auto it = m_receiverResourcelist.begin(); it != m_receiverResourcelist.end(); ++it)
{
//Take mutex for the resource since we are going to interact with shared resources
//std::lock_guard<std::mutex> guard((*it).mtx);
if (it->Receiver->SupportsLocator(*lit))
{
//Supported! Take mutex and update lists - We maintain reader/writer discrimination just in case
// mp_receiver 与 endp关联
it->mp_receiver->associateEndpoint(endp);
// end association between reader/writer and the receive resources
}
}
//Finished iteratig through all ListenResources for a single Locator (from the parameter list).
//Since this function is called after checking with NetFactory we do not have to create any more resource.
}
return true;
}
这个函数主要是将mp_receiver(messagereceiver 的对象) 与 endp关联
上面函数通过LocatorList_t,找到对应的receiverresource,然后将endpoint关联到receiverresource的messagereceiver上去。
在receiverresource(也就是接收消息的socket)接到消息之后,将会让和receiverresource相关的messagereceiver处理消息,messagerreceiver做消息解析之后,找到接收者,也就是endpoint(rtpsreader或者rtpswriter),这些endpoint会做处理。
步骤7:
StatefulWriter::StatefulWriter()
{
init(pimpl, att);
}
调用了初始化函数
void StatefulWriter::init(
RTPSParticipantImpl* pimpl,
const WriterAttributes& att)
{
const RTPSParticipantAttributes& part_att = pimpl->getRTPSParticipantAttributes();
auto push_mode = PropertyPolicyHelper::find_property(att.endpoint.properties, "fastdds.push_mode");
m_pushMode = !((nullptr != push_mode) && ("false" == *push_mode));
//心跳
periodic_hb_event_ = new TimedEvent(
pimpl->getEventResource(),
[&]() -> bool
{
return send_periodic_heartbeat();
},
TimeConv::Time_t2MilliSecondsDouble(m_times.heartbeatPeriod));
//nack response
nack_response_event_ = new TimedEvent(
pimpl->getEventResource(),
[&]() -> bool
{
perform_nack_response();
return false;
},
TimeConv::Time_t2MilliSecondsDouble(m_times.nackResponseDelay));
//positive_acks就是收到消息之后马上回个消息
if (disable_positive_acks_)
{
//收到一个消息,设置超时时间,到时间发送ack
ack_event_ = new TimedEvent(
pimpl->getEventResource(),
[&]() -> bool
{
return ack_timer_expired();
},
att.keep_duration.to_ns() * 1e-6); // in milliseconds
}
for (size_t n = 0; n < att.matched_readers_allocation.initial; ++n)
{
matched_readers_pool_.push_back(new ReaderProxy(m_times, part_att.allocation.locators, this));
}
}
每个StatefulWriter,有三个TimedEvent:
periodic_hb_event_
nack_response_event_
ack_event_ (可选)
periodic_hb_event_ 是edp 心跳
ack 是正向确认,正向确认就是接收方对每个成功接收的数据包发送ack 包
nack是反向确认,就是接收方只在发现数据包丢失或损坏时发送nack包
nack的优势是在稳定网络的情况下也就是当大部分数据包能被正确接收的时候,可以减少带宽占用,缺点在于如果网络不稳定,那么需要频繁发送nack,要求重传
每个StatefulWriter 都会发送heartbeat 和 nack 消息
periodic_hb_event_ 负责发送heartbeat
nack_response_event_ 负责发送nack 消息
如果 disable_positive_acks_ 被设置为true,那么ack_event_ 也会被创建
ack_event_ 并不发送消息,这个TimedEvent会设置一个超时时间,如果没有收到nack消息,则认为之前发送的消息已经被收到了。
那么可能会出现一种情况,就是nack消息可能还没被收到或者已经丢失,这时候认为之前发送的消息已经被收到,可能会丢失消息,那么这个statefulwriter,就不再是个reliable 的writer,会是一个best effort的writer。
关于这个参数 涉及到一个qos 就是fastdds 的一个扩展qos
DISABLEPOSITIVEACKS_QOS_POLICY_ID
所谓扩展qos就是针对标准qos而言的qos,标准qos就是所有针对dds 的开源闭源的代码都需要实现的qos,3种开源版本(fastdds,cyclondds,opendds),和一种商业闭源版本(connex dds)都支持标准qos,扩展qos 是选择实现的。
步骤8创建EDP的Reader(StatefulReader):
bool RTPSParticipantImpl::createReader(
RTPSReader** ReaderOut,
ReaderAttributes& param,
const std::shared_ptr<IPayloadPool>& payload_pool,
ReaderHistory* hist,
ReaderListener* listen,
const EntityId_t& entityId,
bool isBuiltin,
bool enable)
{
if (!payload_pool)
{
EPROSIMA_LOG_ERROR(RTPS_PARTICIPANT, "Trying to create reader with null payload pool");
return false;
}
auto callback = [hist, listen, &payload_pool, this]
(const GUID_t& guid, ReaderAttributes& param, IPersistenceService* persistence,
bool is_reliable) -> RTPSReader*
{
if (is_reliable)
{
if (persistence != nullptr)
{
return new StatefulPersistentReader(this, guid, param, payload_pool, hist, listen, persistence);
}
else
{
return new StatefulReader(this, guid, param, payload_pool, hist, listen);
}
}
else
{
if (persistence != nullptr)
{
return new StatelessPersistentReader(this, guid, param, payload_pool, hist, listen,
persistence);
}
else
{
return new StatelessReader(this, guid, param, payload_pool, hist, listen);
}
}
};
return create_reader(ReaderOut, param, entityId, isBuiltin, enable, callback);
}
这个函数在介绍PDP初始化的时候介绍过,在这儿主要是创建了一个回调函数,回调函数后续被调用到用来创建StatefulReader
步骤9:
static bool create_edp_reader(
RTPSParticipantImpl* participant,
const std::string& topic_name,
const EntityId_t& entity_id,
const HistoryAttributes& history_att,
ReaderAttributes& ratt,
ReaderListener* listener,
std::shared_ptr<ITopicPayloadPool>& payload_pool,
ReaderHistoryPair& edp_reader)
{
RTPSReader* raux = nullptr;
payload_pool = create_payload_pool(topic_name, history_att, true);
edp_reader.second = new ReaderHistory(history_att);
bool created =
participant->createReader(&raux, ratt, payload_pool, edp_reader.second, listener, entity_id,
true);
if (created)
{
edp_reader.first = dynamic_cast<StatefulReader*>(raux);
}
else
{
delete(edp_reader.second);
edp_reader.second = nullptr;
release_payload_pool(payload_pool, history_att, true);
}
return created;
}
这个函数主要是创建Reader,先创建一个ReaderHistory,里面有一个缓存来存储消息,每一个reader要发送的消息都先放入这个缓存中,fastdds 会配置一个机制来管理缓存。
调用到RTPSParticipantImpl::createReader函数,这个函数在PDP创建reader的时候,经过详细解析,可以参考一下相关内容。
这里调用到了RTPSParticipantImpl的createReader函数,这个函数我们在上一篇中有过详细解析,可以看一下相关内容
步骤10:
这里主要摘出了一些和之前创建statelessreader 不一样的一些代码
template <typename Functor>
bool RTPSParticipantImpl::create_reader(
RTPSReader** reader_out,
ReaderAttributes& param,
const EntityId_t& entity_id,
bool is_builtin,
bool enable,
const Functor& callback)
{
······
//省略部分代码
······
//在这儿创建的是statefulreader
SReader = callback(guid, param, persistence, param.endpoint.reliabilityKind == RELIABLE);
······
//省略部分代码
······
// 在RELIABLE 的情况下,创建sendresources
// edp reader这儿为reliabilityKind == RELIABLE
if (param.endpoint.reliabilityKind == RELIABLE)
{
createSendResources(SReader);
}
//省略部分代码
······
//创建的edp reader的时候enbale 是 true
if (enable)
{
if (!createAndAssociateReceiverswithEndpoint(SReader, request_unique_flows, initial_port, final_port))
{
delete(SReader);
return false;
}
}
······
//省略部分代码
······
return true;
}
这里面首先创建一个reader,创建receiverresource,如果reader 是RELIABLE,创建senderresource,将reader放入m_userReaderList中去。
只有在reader 的参数reliabilityKind == RELIABLE
才会创建senderresource,reader需要把接收到的消息,同步给writer,如果writer发现有丢包,则会重传消息。
步骤11:
StatefulReader::StatefulReader()
{
init(pimpl, att);
}
调用了初始化函数
void StatefulReader::init(
RTPSParticipantImpl* pimpl,
const ReaderAttributes& att)
{
const RTPSParticipantAttributes& part_att = pimpl->getRTPSParticipantAttributes();
for (size_t n = 0; n < att.matched_writers_allocation.initial; ++n)
{
matched_writers_pool_.push_back(new WriterProxy(this, part_att.allocation.locators, proxy_changes_config_));
}
}
初始化这个matched_writers_pool_,这是个缓存池。
WriterProxy 中存储的是这个reader 对应的writer的信息。
matched_writers_pool_这个缓存池是预分配的,当有writer 匹配到这个reader,从缓存池中获取一个item,填入数据,再放到
matched_writers这个vector中去。
1.6类图
classDiagram
RTPSParticipantImpl *-- BuiltinProtocols
BuiltinProtocols *-- PDP
PDP *-- RTPSWriter
PDP *-- RTPSReader
BuiltinProtocols *-- WLP
PDP *-- EDP
EDP <-- EDPSimple
EDPSimple *--StatefulWriter
EDPSimple *--StatefulReader
class RTPSParticipantImpl {
+BuiltinProtocols mp_builtinProtocols
}
class BuiltinProtocols {
+PDP mp_PDP
+WLP mp_WLP
}
class PDP {
+EDP mp_EDP
}
class RTPSWriter {
}
class RTPSReader {
}
class EDPSimple {
std::pair<StatefulWriter*,WriterHistory*> publications_writer_
std::pair<StatefulWriter*,WriterHistory*> subscriptions_writer_
std::pair<StatefulReader*,ReaderHistory*> publications_reader_
std::pair<StatefulReader*,ReaderHistory*> subscriptions_reader_
}
这是类图,RTPSParticipant 包含了BuiltinProtocols对象,BuiltinProtocols负责管理内置的协议,pdp ,edp 和 wlp。
而EDP,是由PDP管理的。
PDP中一般配置有RTPSWriter和RTPSReader,用来读取和发送pdp消息。
EDP中一半配置有RTPSWriter和RTPSReader,用来互相发送EDP消息。
目录:
第一篇 一个例子
车载消息中间件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