车载消息中间件FastDDS 源码解析(一)FastDDS 介绍和使用
车载消息中间件FastDDS 源码解析(二)RtpsParticipant的创建(上)
车载消息中间件FastDDS 源码解析(三)RtpsParticipant的创建(中)
6.createReceiverResources
根据之前获取的metatrafficMulticastLocatorList,metatrafficUnicastLocatorList,defaultUnicastLocatorList,defaultMulticastLocatorList来创建 ReceiverResources
arduino
createReceiverResources(m_att.builtin.metatrafficMulticastLocatorList, true, false);
createReceiverResources(m_att.builtin.metatrafficUnicastLocatorList, true, false);
createReceiverResources(m_att.defaultUnicastLocatorList, true, false);
createReceiverResources(m_att.defaultMulticastLocatorList, true, false);
6.1createReceiverResources的时序图
-
createReceiverResources主要干了2件事,创建receiverresource 和 对应的messagereceiver
为每个Locator 创建ReceiverResource,如果receiverresource创建失败(主要原因可能是端口号被占用),更改端口号,继续创建,试100次,为每个receiverResource 创建一个messagereceiver,receiverresource和messagereceiver,存放到RTPSParticipantImpl的 m_receiverResourcelist里面去。更改端口号可能导致pdp阶段无法建连
-
BuildReceiverResources 这个函数里面主要,根据已经注册的Transports :mRegisteredTransports, new ReceiverResource
-
new ReceiverResource 这个初始化函数,主要是调用了Transport的 OpenInputChannel函数,我们以UDPv4Transport 为例继续往下看
-
UDPv4Transport 的OpenInputChannel 函数 主要是干了2件事
a.OpenAndBindInputSockets 主要是创建了对应的socket 和 channelresource,这个socket 和 channelresource 是一一对应的,
主要根据 locator 的port 和 interface_whitelist_ 中的ip 创建channelResource
b.针对多播的情况一般需要新建一个 channelResource
-
UDPTransportInterface 的函数OpenAndBindInputSockets 主要调用CreateInputChannelResource,创建channelresource
将channelresource 存入mInputSockets,mInputSockets以port 为key,以vector为value
-
UDPTransportInterface的函数CreateInputChannelResource 主要干了2件事:
a.OpenAndBindInputSocket 主要是创建了一个socket
b.在OpenAndBindInputSocket 创建的socket的基础上new 了一个UDPChannelResource,每个UDPChannelResource 配置一个thread,循环接收消息
-
OpenAndBindInputSocket 创建socket, 配置socket
-
FlowControllerFactory 的init函数 这个在第三篇中详细介绍
-
new 了 BuiltinProtocols 这个在第四篇中详细介绍
6.2createReceiverResources的源码
先看步骤1:
scss
bool RTPSParticipantImpl::createReceiverResources(
LocatorList_t& Locator_list,
bool ApplyMutation,
bool RegisterReceiver)
{
std::vector<std::shared_ptr<ReceiverResource>> newItemsBuffer;
bool ret_val = Locator_list.empty();
······
// 为每个Locator 创建ReceiverResource
for (auto it_loc = Locator_list.begin(); it_loc != Locator_list.end(); ++it_loc)
{
// 如果receiverresource创建失败(主要原因可能是端口号被占用),更改端口号,继续创建,
// 尝试次数m_att.builtin.mutation_tries
bool ret = m_network_Factory.BuildReceiverResources(*it_loc, newItemsBuffer, max_receiver_buffer_size);
if (!ret && ApplyMutation)
{
uint32_t tries = 0;
while (!ret && (tries < m_att.builtin.mutation_tries))
{
tries++;
applyLocatorAdaptRule(*it_loc);
ret = m_network_Factory.BuildReceiverResources(*it_loc, newItemsBuffer, max_receiver_buffer_size);
}
}
ret_val |= !newItemsBuffer.empty();
// 为每个receiverResource 创建一个messagereceiver
for (auto it_buffer = newItemsBuffer.begin(); it_buffer != newItemsBuffer.end(); ++it_buffer)
{
std::lock_guard<std::mutex> lock(m_receiverResourcelistMutex);
//Push the new items into the ReceiverResource buffer
m_receiverResourcelist.emplace_back(*it_buffer);
//Create and init the MessageReceiver
auto mr = new MessageReceiver(this, (*it_buffer)->max_message_size());
//
m_receiverResourcelist.back().mp_receiver = mr;
//Start reception
// 将receiverresource 与 MessageReceiver 关联,关联后可以接收message了
if (RegisterReceiver)
{
m_receiverResourcelist.back().Receiver->RegisterReceiver(mr);
}
}
newItemsBuffer.clear();
}
return ret_val;
}
bool RTPSParticipantImpl::createReceiverResources()
这个函数,主要干了2件事
1.根据LocatorList_t& Locator_list ,通过传入的Locator来创建ReceiverResource,这个ReceiverResource主要是一个socket 和这个socket对应的参数,
2.创建MessageReceiver来处理,socket收到的消息都是交给MessageReceiver处理的。
先看如何创建ReceiverResource,NetworkFactory::BuildReceiverResources来创建ReceiverResource
步骤2:BuildReceiverResources
c
bool NetworkFactory::BuildReceiverResources(
Locator_t& local,
std::vector<std::shared_ptr<ReceiverResource>>& returned_resources_list,
uint32_t receiver_max_message_size)
{
bool returnedValue = false;
for (auto& transport : mRegisteredTransports)
{
if (transport->IsLocatorSupported(local))
{
// 有没有相应的socket
if (!transport->IsInputChannelOpen(local))
{
uint32_t max_recv_buffer_size = (std::min)(
transport->max_recv_buffer_size(),
receiver_max_message_size);
std::shared_ptr<ReceiverResource> newReceiverResource = std::shared_ptr<ReceiverResource>(
new ReceiverResource(*transport, local, max_recv_buffer_size));
if (newReceiverResource->mValid)
{
returned_resources_list.push_back(newReceiverResource);
returnedValue = true;
}
}
else
{
returnedValue = true;
}
}
}
return returnedValue;
}
NetworkFactory::BuildReceiverResources 的主要参数是Locator_t& local
通过NetworkFactory中注册的transport来创建ReceiverResource,主要new了ReceiverResource
步骤3 new ReceiverResource
scss
ReceiverResource::ReceiverResource(
TransportInterface& transport,
const Locator_t& locator,
uint32_t max_recv_buffer_size)
: Cleanup(nullptr)
, LocatorMapsToManagedChannel(nullptr)
, mValid(false)
, mtx()
, cv_()
, receiver(nullptr)
, max_message_size_(max_recv_buffer_size)
, active_callbacks_(0)
{
// Internal channel is opened and assigned to this resource.
mValid = transport.OpenInputChannel(locator, this, max_message_size_);
if (!mValid)
{
return; // Invalid resource to be discarded by the factory.
}
// Implementation functions are bound to the right transport parameters
Cleanup = [&transport, locator]()
{
transport.CloseInputChannel(locator);
};
LocatorMapsToManagedChannel = [&transport, locator](const Locator_t& locatorToCheck) -> bool
{
return locator.kind == locatorToCheck.kind && transport.DoInputLocatorsMatch(locator, locatorToCheck);
};
}
ReceiverResource::ReceiverResource的主要参数是TransportInterface& transport,const Locator_t& locator,主要调用了transport.OpenInputChannel来创建,
我们这边以UDPv4Transport来举例
步骤4 UDPv4Transport 的OpenInputChannel
rust
bool UDPv4Transport::OpenInputChannel(
const Locator& locator,
TransportReceiverInterface* receiver,
uint32_t maxMsgSize)
{
std::unique_lock<std::recursive_mutex> scopedLock(mInputMapMutex);
if (!is_locator_allowed(locator))
{
return false;
}
bool success = false;
// 保证这个locator有一个对应的socket
if (!IsInputChannelOpen(locator))
{
// 根据 locator 的port 和 interface_whitelist_ 中的ip 创建channelResource
success = OpenAndBindInputSockets(locator, receiver, IPLocator::isMulticast(locator), maxMsgSize);
}
// 针对多播的情况一般需要 需要新建一个 channelResource
// IsInputChannelOpen 主要是看port 有没有对应的 channelResource
if (IPLocator::isMulticast(locator) && IsInputChannelOpen(locator))
{
//ip地址
std::string locatorAddressStr = IPLocator::toIPv4string(locator);
ip::address_v4 locatorAddress = ip::address_v4::from_string(locatorAddressStr);
#ifndef _WIN32
// 非windows的系统里面 如果interface_whitelist_ 不为空
if (!is_interface_whitelist_empty())
{
// Either wildcard address or the multicast address needs to be bound on non-windows systems
bool found = false;
// First check if the multicast address is already bound
auto& channelResources = mInputSockets.at(IPLocator::getPhysicalPort(locator));
for (UDPChannelResource* channelResource : channelResources)
{
if (channelResource->interface() == locatorAddressStr)
{
// 能找到相应的channelResource
found = true;
break;
}
}
// Create a new resource if no one is found
if (!found)
{
try
{
// Bind to multicast address
// 如果没有找到UDPChannelResource,创建一个
UDPChannelResource* p_channel_resource;
// locatorAddressStr, locator 一一对应的关系
p_channel_resource = CreateInputChannelResource(locatorAddressStr, locator, true, maxMsgSize, receiver);
mInputSockets[IPLocator::getPhysicalPort(locator)].push_back(p_channel_resource);
// Join group on all whitelisted interfaces
// 配置多播参数
for (auto& ip : interface_whitelist_)
{
// locatorAddress多播地址,ip是本机地址
// socket 加入多播组
p_channel_resource->socket()->set_option(ip::multicast::join_group(locatorAddress, ip));
}
}
------
}
}
else
#endif // ifndef _WIN32
------
return success;
}
UDPv4Transport::OpenInputChannel 主要
1.调用的OpenAndBindInputSockets,根据本地ip地址 创建socket
2.如果是多播,加入多播组,配置相应参数,这里面会根据平台区分,win32的有win32的加入方式,其他平台有其他平台的加入方式。
步骤5 OpenAndBindInputSockets
c
bool UDPTransportInterface::OpenAndBindInputSockets(
const Locator& locator,
TransportReceiverInterface* receiver,
bool is_multicast,
uint32_t maxMsgSize)
{
······
try
{
//interface_whitelist_ 为空,把地址0 放入interface_whitelist_
std::vector<std::string> vInterfaces = get_binding_interfaces_list();
//本机的ip地址,根据本机的ip地址,创建ChannelResource,ChannelResource的interface 是ip地址
for (std::string sInterface : vInterfaces)
{
UDPChannelResource* p_channel_resource;
// 主要用到的是 locator 的port 和 interface_whitelist_的ip
p_channel_resource = CreateInputChannelResource(sInterface, locator, is_multicast, maxMsgSize, receiver);
mInputSockets[IPLocator::getPhysicalPort(locator)].push_back(p_channel_resource);
}
}
······
return true;
}
主要干了2件事
1.get_binding_interfaces_list 获取本地ip地址,
2.使用获取的ip地址CreateInputChannelResource创建channelresource ,channelresource主要是socket,和socket 对应的参数,每个channelresource 有个线程,不断接收消息,收到消息之后交给messagereceiver处理。
c
std::vector<std::string> UDPv4Transport::get_binding_interfaces_list()
{
std::vector<std::string> vOutputInterfaces;
// 如果白名单为空,则将0.0.0.0放入
if (is_interface_whitelist_empty())
{
vOutputInterfaces.push_back(s_IPv4AddressAny);
}
else
{
for (auto& ip : interface_whitelist_)
{
vOutputInterfaces.push_back(ip.to_string());
}
}
return vOutputInterfaces;
}
udpv4transport 的属性 interface_whitelist_ 最少包含一个ip地址
如果为空,会用0.0.0.0填充进去
步骤6:根据locator 调用CreateInputChannelResource
arduino
UDPChannelResource* UDPTransportInterface::CreateInputChannelResource(
const std::string& sInterface,
const Locator& locator,
bool is_multicast,
uint32_t maxMsgSize,
TransportReceiverInterface* receiver)
{
// 生成一个socket
eProsimaUDPSocket unicastSocket = OpenAndBindInputSocket(sInterface,
IPLocator::getPhysicalPort(locator), is_multicast);
// 每个channelresource 有个线程,不断接收消息
UDPChannelResource* p_channel_resource = new UDPChannelResource(this, unicastSocket, maxMsgSize, locator,
sInterface, receiver);
return p_channel_resource;
}
1.调用UDPv4Transport::OpenAndBindInputSocket创建一个socket
2.创建UDPChannelResource的时候,会创建一个线程不断循环,接收socket数据
步骤7:
scss
eProsimaUDPSocket UDPv4Transport::OpenAndBindInputSocket(
const std::string& sIp,
uint16_t port,
bool is_multicast)
{
eProsimaUDPSocket socket = createUDPSocket(io_service_);
getSocketPtr(socket)->open(generate_protocol());
if (mReceiveBufferSize != 0)
{
getSocketPtr(socket)->set_option(socket_base::receive_buffer_size(mReceiveBufferSize));
}
if (is_multicast)
{
getSocketPtr(socket)->set_option(ip::udp::socket::reuse_address(true));
#if defined(__QNX__)
getSocketPtr(socket)->set_option(asio::detail::socket_option::boolean<
ASIO_OS_DEF(SOL_SOCKET), SO_REUSEPORT>(true));
#endif // if defined(__QNX__)
}
getSocketPtr(socket)->bind(generate_endpoint(sIp, port));
return socket;
}
创建socket,配置参数
上面这些代码主要是为了创建接收的socket,如果有白名单,则根据白名单和本地ip地址的交集来创建socket,如果白名单为空,则根据ip地址0.0.0.0来创建接收的socket。
多播 和 单播的端口号是不一样的。builtin 的单播的端口号,和非builtin的单播的端口号也是不一样的
css
UDPChannelResource::UDPChannelResource(
)
{
thread(std::thread(&UDPChannelResource::perform_listen_operation, this, locator));
}
在这儿就是创建了一个线程,不断接收数据
6.2类图
createReceiverResources(m_att.builtin.metatrafficMulticastLocatorList, true, false);
builtin 是每个participant 自带的,builtin.metatrafficMulticastLocatorList的ip地址默认是239.255.0.1
这里面应该是创建了多个socket监听,如果有白名单ip,地址就对白名单ip地址和本地ip地址(包括回环地址127.0.0.1)的交集监听,如果没有就对0.0.0.0的ip地址做监听,这些ip地址,加入到 239.255.0.1的多播地址group
createReceiverResources(m_att.builtin.metatrafficUnicastLocatorList, true, false);
builtin.metatrafficUnicastLocatorList 的ip地址默认是0
这里面应该是创建了多个socket监听,如果有白名单ip,地址就对白名单ip地址和本地ip地址(包括回环地址127.0.0.1)的交集监听,如果没有就对0.0.0.0的ip地址做监听
createReceiverResources(m_att.defaultUnicastLocatorList, true, false); createReceiverResources(m_att.defaultMulticastLocatorList, true, false);
7.flow_controller
在RTPSParticipantImpl的构造函数当中,初始化了flow_controller_factory_,同时根据配置参数,register_flow_controller,
下面是RTPSParticipantImpl 构造函数中的相关代码
ini
//初始化flow_controller_factory_
flow_controller_factory_.init(this);
// Support old API
if (PParam.throughputController.bytesPerPeriod != UINT32_MAX && PParam.throughputController.periodMillisecs != 0)
{
fastdds::rtps::FlowControllerDescriptor old_descriptor;
old_descriptor.name = guid_str_.c_str();
old_descriptor.max_bytes_per_period = PParam.throughputController.bytesPerPeriod;
old_descriptor.period_ms = PParam.throughputController.periodMillisecs;
flow_controller_factory_.register_flow_controller(old_descriptor);
}
// Register user's flow controllers.
//注册自定义的flow_controller
for (auto flow_controller_desc : m_att.flow_controllers)
{
flow_controller_factory_.register_flow_controller(*flow_controller_desc.get());
}
主要干了2件事
1.初始化flow_controller_factory_,在这里初始化了3-4个默认的flow_controller
2.注册自定义的flow_controller
arduino
void FlowControllerFactory::init(
fastrtps::rtps::RTPSParticipantImpl* participant)
{
participant_ = participant;
// Create default flow controllers.
// PureSyncFlowController -> used by
besteffort writers.
flow_controllers_.insert(decltype(flow_controllers_)::value_type(
pure_sync_flow_controller_name,
std::unique_ptr<FlowController>(
new FlowControllerImpl<FlowControllerPureSyncPublishMode,
FlowControllerFifoSchedule>(participant_, nullptr))));
// SyncFlowController -> used by rest of besteffort writers.
flow_controllers_.insert(decltype(flow_controllers_)::value_type(
sync_flow_controller_name,
std::unique_ptr<FlowController>(
new FlowControllerImpl<FlowControllerSyncPublishMode,
FlowControllerFifoSchedule>(participant_, nullptr))));
// AsyncFlowController
flow_controllers_.insert(decltype(flow_controllers_)::value_type(
async_flow_controller_name,
std::unique_ptr<FlowController>(
new FlowControllerImpl<FlowControllerAsyncPublishMode,
FlowControllerFifoSchedule>(participant_, nullptr))));
#ifdef FASTDDS_STATISTICS
flow_controllers_.insert(decltype(flow_controllers_)::value_type(
async_statistics_flow_controller_name,
std::unique_ptr<FlowController>(
new FlowControllerImpl<FlowControllerAsyncPublishMode,
FlowControllerFifoSchedule>(participant_, nullptr))));
#endif // ifndef FASTDDS_STATISTICS
}
//在初始化函数中初始化了3个默认的flow_controller,这3个 flow_controller,
FlowControllerImpl<FlowControllerPureSyncPublishMode,FlowControllerFifoSchedule>
FlowControllerImpl<FlowControllerSyncPublishMode,FlowControllerFifoSchedule>
FlowControllerImpl<FlowControllerAsyncPublishMode,FlowControllerFifoSchedule>
如果有STATISTICS的话,会初始化一个适用于STATISTICS的flow_controller
这3个看字面意思是这样的,
第一个纯同步的,先进先出(fifo)的发送模式,纯同步就是完全没有异步发送方式
第二个同步的,先进先出(fifo)的发送模式,如果同步发送不成功,则异步发送
第三个异步的,先进先出(fifo)的发送模式,异步的就是没有同步发送的方式,都是异步发送
FlowControllerFifoSchedule表示的是发送模式,什么意思哪,就是我们有个发送消息的队列,所有的发送消息进队列,然后发送顺序是先进先出
php
void FlowControllerFactory::register_flow_controller (
const FlowControllerDescriptor& flow_controller_descr)
{
if (flow_controllers_.end() != flow_controllers_.find(flow_controller_descr.name))
{
EPROSIMA_LOG_ERROR(RTPS_PARTICIPANT,
"Error registering FlowController " << flow_controller_descr.name << ". Already registered");
return;
}
//如果设置流量控制
if (0 < flow_controller_descr.max_bytes_per_period)
{
switch (flow_controller_descr.scheduler)
{
case FlowControllerSchedulerPolicy::FIFO:
flow_controllers_.insert(decltype(flow_controllers_)::value_type(
flow_controller_descr.name,
std::unique_ptr<FlowController>(
new FlowControllerImpl<FlowControllerLimitedAsyncPublishMode,
FlowControllerFifoSchedule>(participant_, &flow_controller_descr))));
break;
case FlowControllerSchedulerPolicy::ROUND_ROBIN:
flow_controllers_.insert(decltype(flow_controllers_)::value_type(
flow_controller_descr.name,
std::unique_ptr<FlowController>(
new FlowControllerImpl<FlowControllerLimitedAsyncPublishMode,
FlowControllerRoundRobinSchedule>(participant_,
&flow_controller_descr))));
break;
case FlowControllerSchedulerPolicy::HIGH_PRIORITY:
flow_controllers_.insert(decltype(flow_controllers_)::value_type(
flow_controller_descr.name,
std::unique_ptr<FlowController>(
new FlowControllerImpl<FlowControllerLimitedAsyncPublishMode,
FlowControllerHighPrioritySchedule>(participant_,
&flow_controller_descr))));
break;
case FlowControllerSchedulerPolicy::PRIORITY_WITH_RESERVATION:
flow_controllers_.insert(decltype(flow_controllers_)::value_type(
flow_controller_descr.name,
std::unique_ptr<FlowController>(
new FlowControllerImpl<FlowControllerLimitedAsyncPublishMode,
FlowControllerPriorityWithReservationSchedule>(participant_,
&flow_controller_descr))));
break;
default:
assert(false);
}
}
else
{
switch (flow_controller_descr.scheduler)
{
case FlowControllerSchedulerPolicy::FIFO:
flow_controllers_.insert(decltype(flow_controllers_)::value_type(
flow_controller_descr.name,
std::unique_ptr<FlowController>(
new FlowControllerImpl<FlowControllerAsyncPublishMode,
FlowControllerFifoSchedule>(participant_, &flow_controller_descr))));
break;
case FlowControllerSchedulerPolicy::ROUND_ROBIN:
flow_controllers_.insert(decltype(flow_controllers_)::value_type(
flow_controller_descr.name,
std::unique_ptr<FlowController>(
new FlowControllerImpl<FlowControllerAsyncPublishMode,
FlowControllerRoundRobinSchedule>(participant_,
&flow_controller_descr))));
break;
case FlowControllerSchedulerPolicy::HIGH_PRIORITY:
flow_controllers_.insert(decltype(flow_controllers_)::value_type(
flow_controller_descr.name,
std::unique_ptr<FlowController>(
new FlowControllerImpl<FlowControllerAsyncPublishMode,
FlowControllerHighPrioritySchedule>(participant_,
&flow_controller_descr))));
break;
case FlowControllerSchedulerPolicy::PRIORITY_WITH_RESERVATION:
flow_controllers_.insert(decltype(flow_controllers_)::value_type(
flow_controller_descr.name,
std::unique_ptr<FlowController>(
new FlowControllerImpl<FlowControllerAsyncPublishMode,
FlowControllerPriorityWithReservationSchedule>(participant_,
&flow_controller_descr))));
break;
default:
assert(false);
}
}
}
//这个函数是注册自定义的flow_controller,这个需要为flow_controller配置一些参数,然后FlowControllerFactory根据配置的参数生成不同类型的FlowController
flow_controller_descr.max_bytes_per_period 这个参数是对传输限速,
FlowControllerSchedulerPolicy有四种类型,如下所示:
FlowControllerSchedulerPolicy::FIFO
FlowControllerSchedulerPolicy::ROUND_ROBIN
FlowControllerSchedulerPolicy::HIGH_PRIORITY
FlowControllerSchedulerPolicy::PRIORITY_WITH_RESERVATION
除了在上面介绍的fifo之外,还有ROUND_ROBIN ,HIGH_PRIORITY,PRIORITY_WITH_RESERVATION
ROBIN是轮询,
HIGH_PRIORITY按照优先级发送,
PRIORITY_WITH_RESERVATION是按照优先级,同时兼顾公平,每个writer有个最少的带宽
每一个writer都有一个配套的flow_controller,flow_controller控制了writer 发送消息的策略和行为。
总结一下,关于RtpsParticipant的创建,我们围绕RtpsParticipant的构造函数,写了3篇,RtpsParticipant的构造函数的6个部分,介绍了5个,还有1个BuiltinProtocols还没有介绍,这个部分内容很多将会分2篇介绍。
车载消息中间件FastDDS 源码解析(一)FastDDS 介绍和使用