车载消息中间件FastDDS 源码解析(三)RtpsParticipant的创建(中)
3.Transport 的初始化
这是RTPSParticipantImpl初始化的第一部分
主要是RTPSParticipantImpl构造函数代码的37-95行
内容很多我们慢慢分析
3.1Transport 的简单介绍
NetworkFactory 主要是为participant 管理发送接收消息的一个类。
NetworkFactory看名字就知道是工厂设计模式,主要就是管理transport,有个vector,std::vector[std::unique_ptrfastdds::rtps::TransportInterface]() mRegisteredTransports 存储的就是他管理的transport。
每个NetworkFactory,都配置有Transport,三类transport :
- udptransport(v4,v6)//udp通路的transport,包含udpv4transport(支持ipv4协议) 和udpv6transport(支持ipv6协议)
- tcptransport(v4,v6)//tcp通路的transport,包含tcpv4transport(支持ipv4协议) 和tcpv6transport(支持ipv6协议)
- shmtransport
每个NetworkFactory,有个属性 mRegisteredTransports,就是这个networkFactory 有拥有的transport。
fastdds 默认支持的是udpv4 和shm,udpv4就是ipv4的udp协议。shm就是共享内存,这个在同一物理节点之间跨进程传输。
一般而言udpv4 和 shm 就够了,udpv4负责跨物理设备的传输,也就是网络传输,shm负责跨进程,进程内的传输。
对于udpv6 ,tcpv4,tcpv6这些可以通过参数配置支持。
我们这里会对udpv4做详细的介绍,至于udpv6 ,tcpv4,tcpv6这些差不多类似,这里不做展开,shm传输会在专门章节做介绍。
我们看上面RTPSParticipantImpl构造函数的 37行-63行代码,这块就是配置参数,创建默认的udpv4transport,如果支持shm就创建shmtransport,创建完成后注册到NetworkFactory内,放入mRegisteredTransports中去。
3.2Transport 的在RTPSParticipantImpl中的参数配置
看一下RTPSParticipantImpl构造函数的代码37行
PParam.useBuiltinTransports 这个参数是个比较关键的参数,在参数配置的时候需要配置这个参数,如果为true,就会使用默认的BuiltinTransport,使用物理设备的所有网卡的ip地址。如果为false,我们在配置我们的participant的时候有时候需要按照我们的具体情况来配置ip地址。
可能大家对此没有概念,我举了例子大家就明白了:对于手机来说 ,有多个ip地址:我们截取一下手机的ifconfig 的部分内容,如下图所示:
这个手机的话有这几个地址
- wlan0地址:这个地址就是局域网地址
- p2p0地址:这个地址就是wifi p2p的地址,就是wifi直连的地址
- 还有手机的移动网络的地址
这些地址一般都有2个地址:ipv4地址 和ipv6地址。
刚才我们说的配置ip地址,就是选择什么样的ip地址作为我们的tcp,udp的发送地址。
对于手机来说选择wlan ip,p2p ip还是手机移动网络的ip 走的底层器件都是不一样的。
我们希望在局域网中运行我们的程序,我们就选择wlan0地址作为我们的发送地址。我们希望在wifi p2p网络中运行我们的程序,我们就选择p2p0地址作为我们的发送地址。这样功耗内存,网络带宽的使用就不会造成浪费。
PParam.useBuiltinTransports 如果选择true就表示,将会使用所有网卡的ip地址,对于一台手机来说,wifi p2p 还有手机移动网络都进行组网。如果选择flase,则需要我们自己配置transport,那么我们可以自己选择ip地址进行组网,比如说我们希望只在局域网组网,那么我们只需要选择局域网的ip地址,放入transport 的interfacewhitelist中。
这个interfacewhitelist可以在我们初始化的时候进行配置。
我们知道现在的IOT设备比较复杂,同时对功耗要求又比较高,所以基本上interfacewhitelist都需要进行一定的配置,降低功耗节省资源。
3.3udpv4transport的创建
创建udpv4transport 大概分为7步:
如下:
3.3.1时序图
-
RTPSParticipantImpl 调用NetworkFactory的函数RegisterTransport
每个NetworkFactory,都配置有Transport,三类transport :udptransport(v4,v6),tcptransport(v4,v6),shmtransport
每个NetworkFactory,有个属性 mRegisteredTransports,就是这个networkFactory 有的transport,如果不做特殊处理,一般只有udpv4transport
RegisterTransport 主要干了3件事情:
a.create Transport。下面我们都以udpv4transport为例来说明:
b.Transport 初始化
c.参数配置
-
create_transport 创建了udpv4transport,调用了new udpv4transport
-
在new udpv4transport的过程中,如果interfaceWhiteList 不为空,调用了get_ipv4s
-
get_ipv4s,将获取到的ip地址和interfaceWhiteList的并集 放入interface_whitelist_中
get_ipv4s获取了本机ip地址(包括回环地址 ipv4 一般是 127.0.0.1)
-
UDPTransportInterface::init 初始化udpv4transport
在这个函数中会调用UDPv4Transport的get_ips的函数
-
UDPv4Transport的get_ips调用UDPv4Transport的get_ipv4s函数
-
UDPv4Transport的get_ipv4s函数
3.3.2源码解析
我们看一下具体是如何创建udpv4transport的:
步骤1
调用NetworkFactory::RegisterTransport (代码47行,92行)
bool NetworkFactory::RegisterTransport(
const TransportDescriptorInterface* descriptor,
const fastrtps::rtps::PropertyPolicy* properties)
{
bool wasRegistered = false;
uint32_t minSendBufferSize = std::numeric_limits<uint32_t>::max();
// create Transport
std::unique_ptr<TransportInterface> transport(descriptor->create_transport());
if (transport)
{
//Transport 初始化
if (transport->init(properties))
{
minSendBufferSize = transport->get_configuration()->min_send_buffer_size();
mRegisteredTransports.emplace_back(std::move(transport));
wasRegistered = true;
}
if (wasRegistered)
{
if (descriptor->max_message_size() < maxMessageSizeBetweenTransports_)
{
maxMessageSizeBetweenTransports_ = descriptor->max_message_size();
}
if (minSendBufferSize < minSendBufferSize_)
{
minSendBufferSize_ = minSendBufferSize;
}
}
}
return wasRegistered;
}
上面这个函数主要是干了2件事
-
TransportDescriptorInterface 调用create_transport,创建transport
-
transport->init初始化,对transport进行初始化做参数配置。
然后将transport放入mRegisteredTransports中。
步骤2 create_transport
调用到UDPv4TransportDescriptor::create_transport
TransportInterface* UDPv4TransportDescriptor::create_transport() const
{
return new UDPv4Transport(*this);
}
这个函数通过UDPv4TransportDescriptor来create_transport,UDPv4TransportDescriptor这个是UDPv4Transport的配置类,通过UDPv4TransportDescriptor来create_transport。
步骤3 new UDPv4Transport
UDPv4Transport::UDPv4Transport(
const UDPv4TransportDescriptor& descriptor)
: UDPTransportInterface(LOCATOR_KIND_UDPv4)
, configuration_(descriptor)
{
mSendBufferSize = descriptor.sendBufferSize;
mReceiveBufferSize = descriptor.receiveBufferSize;
//如果interfaceWhiteList不为空
if (!descriptor.interfaceWhiteList.empty())
{
const auto white_begin = descriptor.interfaceWhiteList.begin();
const auto white_end = descriptor.interfaceWhiteList.end();
std::vector<IPFinder::info_IP> local_interfaces;
// 获取本机网卡的ipv4地址,true 返回回环地址
// ipv4的回环地址 是 127.0.0.1 ipv6的回环地址是 0:0:0:1
// 本机地址 有好几个:wifi地址,4g地址,wifi p2p地址等
get_ipv4s(local_interfaces, true);
for (const IPFinder::info_IP& infoIP : local_interfaces)
{
//将descriptor.interfaceWhiteList 和local_interfaces的交集放入 interface_whitelist_
if (std::find(white_begin, white_end, infoIP.name) != white_end)
{
interface_whitelist_.emplace_back(ip::address_v4::from_string(infoIP.name));
}
}
// 如果没有获取本地ip地址,就将192.0.2.0放入interface_whitelist_
if (interface_whitelist_.empty())
{
EPROSIMA_LOG_ERROR(TRANSPORT, "All whitelist interfaces were filtered out");
�� interface_whitelist_.emplace_back(ip::address_v4::from_string("192.0.2.0"));
}
}
}
这个函数主要是对参数的处理
我们重点关注一下interfaceWhiteList的处理:interfaceWhiteList是应用配置的参数,我们刚才讲到,物理设备有很多的ip,应用可能希望只以其中的一部分ip来进行组网,那么interfaceWhiteList的配置主要就是干的这个事情。上面函数对于interfaceWhiteList的处理主要干了这2件事情:
-
获取本地的ip地址
-
本地 IP地址与interfaceWhiteList的ip交集,放入interface_whitelist。如果interfaceWhiteList为空 interface_whitelist 也为空
这儿如果对初始化的时候设置的interfaceWhiteList进行过滤,只有本地设备有的ip地址才是有效的地址。
步骤4:get_ipv4s 获取本地的ip地址
static void get_ipv4s(
std::vector<IPFinder::info_IP>& locNames,
bool return_loopback = false)
{
//IP4_LOCAL 指的就是127.0.0.1
//获取地址后将ipv6的地址删除掉
IPFinder::getIPs(&locNames, return_loopback);
auto new_end = remove_if(locNames.begin(),
locNames.end(),
[](IPFinder::info_IP ip)
{
return ip.type != IPFinder::IP4 && ip.type != IPFinder::IP4_LOCAL;
});
//
//本机的ipv4 地址,包括127.0.0.1
locNames.erase(new_end, locNames.end());
std::for_each(locNames.begin(), locNames.end(), [](IPFinder::info_IP& loc)
{
loc.locator.kind = LOCATOR_KIND_UDPv4;
});
}
上面函数主要是获取本地的ip地址,因为我们现在看的是UDPV4Transport的创建,所以我们只关心本地的ipV4地址,其他地址都会丢弃,如果是UDPV6Transport的创建,则会保存ipv6的地址,丢弃其他地址。
上面函数主要干了2件事情
- 获取本地ip地址
- 丢掉ipv6地址
ip.type一般有4种,IP4,IP6,IP4_LOCAL, IP6_LOCAL 4种,IP4_LOCAL就是本地ipv4地址
locator.kind一般分为5种
-
/// UDP over IPv4 locator kind
#define LOCATOR_KIND_UDPv4 1
-
/// UDP over IPv6 locator kind
#define LOCATOR_KIND_UDPv6 2
-
/// TCP over IPv4 kind
#define LOCATOR_KIND_TCPv4 4
-
/// TCP over IPv6 locator kind
#define LOCATOR_KIND_TCPv6 8
-
/// Shared memory locator kind
#define LOCATOR_KIND_SHM 16
本地ip地址获取之后,对locator做一些参数配置
因为各个平台提供的获取本地ip地址的接口不太一样,所以需要根据各个平台不同来使用不同的函数,这儿具体不展开了,有兴趣的可以查看源码。
大概分为这几类:
- windows 获取ip
- linux 获取ip
- qnx获取ip
步骤5 Transport的init函数
UDPTransportInterface 是UDPv4Transport 父类,UDPv4Transport 的init 函数就是UDPTransportInterface::init函数
bool UDPTransportInterface::init(
const fastrtps::rtps::PropertyPolicy*)
{
if (configuration()->sendBufferSize == 0 || configuration()->receiveBufferSize == 0)
{
// Check system buffer sizes
ip::udp::socket socket(io_service_);
socket.open(generate_protocol());
if (configuration()->sendBufferSize == 0)
{
socket_base::send_buffer_size option;
socket.get_option(option);
set_send_buffer_size(static_cast<uint32_t>(option.value()));
if (configuration()->sendBufferSize < s_minimumSocketBuffer)
{
set_send_buffer_size(s_minimumSocketBuffer);
mSendBufferSize = s_minimumSocketBuffer;
}
}
if (configuration()->receiveBufferSize == 0)
{
socket_base::receive_buffer_size option;
socket.get_option(option);
set_receive_buffer_size(static_cast<uint32_t>(option.value()));
if (configuration()->receiveBufferSize < s_minimumSocketBuffer)
{
set_receive_buffer_size(s_minimumSocketBuffer);
mReceiveBufferSize = s_minimumSocketBuffer;
}
}
}
------
// TODO(Ricardo) Create an event that update this list.
// 获取本机的ip地址 放入UDPv4Transport的属性currentInterfaces中
get_ips(currentInterfaces);
return true;
}
//上面这段代码,主要是初始化:sendBufferSize 和receiveBufferSize,检查参数。获取本地的ip地址,存入currentInterfaces
步骤6 UDPv4Transport的get_ips调用UDPv4Transport的get_ipv4s
get_ips 没有其他操作,直接调用了UDPv4Transport的get_ipv4s
步骤7 UDPv4Transport的get_ipv4s 可以参看步骤4
3.4类图
4.mp_event_thr
这是RTPSParticipantImpl初始化的第二部分,主要是上一篇RTPSParticipantImpl初始化代码的98行。
mp_event_thr初始化。只有一行代码在这儿单独列个标题说明,是因为这个对象非常重要。
mp_event_thr 是一个线程,这个线程负责这个RTPSParticipant 中所有的TimedEvent(这个有专门的一篇介绍)。
下面是初始化的代码
void ResourceEvent::init_thread()
{
std::lock_guard<TimedMutex> lock(mutex_);
allow_vector_manipulation_ = false;
stop_.store(false);
resize_collections();
// 初始化thread
thread_ = std::thread(&ResourceEvent::event_service, this);
}
周期事件专用的thread的,这个在发送心跳等场景下使用,轮询各个周期事件,到时间点就执行事件。
5.ip地址和端口号的配置
只有知道ip地址和端口号,才能创建ReceiverResource
先介绍2个概念单播多播
5.1 单播多播的介绍
在这儿先介绍一下单播和多播的概念
多播
多播,也称为“组播”,将网络中同一业务类型主机进行了逻辑上的分组,进行数据收发的时候其数据仅仅在同一分组中进行,其他的主机没有加入此分组不能收发对应的数据。
在广域网上广播的时候,其中的交换机和路由器只向需要获取数据的主机复制并转发数据。主机可以向路由器请求加入或退出某个组,网络中的路由器和交换机有选择地复制并传输数据,将数据仅仅传输给组内的主机。多播的这种功能,可以一次将数据发送到多个主机,又能保证不影响其他不需要(未加入组)的主机的其他通 信。
相对于传统的一对一的单播,多播具有如下的优点:
1、具有同种业务的主机加入同一数据流,共享同一通道,节省了带宽和服务器的优点,具有广播的优点而又没有广播所需要的带宽。
2、服务器的总带宽不受客户端带宽的限制。由于组播协议由接收者的需求来确定是否进行数据流的转发,所以服务器端的带宽是常量,与客户端的数量无关。
3、与单播一样,多播是允许在广域网即Internet上进行传输的,而广播仅仅在同一局域网上才能进行。
单播
就是比较通用的1对1的数据包发送。
dds中单播和多播
在dds 中pdp发现阶段一般都用多播,如果使用单播的话,需要监听好多ip地址的消息,非常低效。而在发现完成之后再用单播发送消息,接收消息,所以在fastdds中,混合使用了多播和单播,总体是使用了单播和多播的长处。
5.2获取默认的locator的时序图
locator是fastdds内部存储ip地址,端口号,IP地址类型的一个对象。
-
如果m_att.builtin.metatrafficMulticastLocatorList 和 m_att.builtin.metatrafficUnicastLocatorList 都是空的
调用get_default_metatraffic_locators函数 获取默认值
这儿会调用NetworkFactory的getDefaultMetatrafficMulticastLocators 获取默认的 metatrafficMulticastLocatorList(builtin 的多播地址) 见2 ,将获取的metatrafficMulticastLocatorList ,进行处理见4
和调用NetworkFactory的getDefaultMetatrafficUnicastLocators 获取默认的metatrafficUnicastLocatorList (builtin的单播地址)
见 6,将获取的metatrafficUnicastLocatorList,进行处理见8
-
NetworkFactory的getDefaultMetatrafficMulticastLocators 会调用UDPv4Transport的getDefaultMetatrafficMulticastLocators
-
UDPv4Transport的getDefaultMetatrafficMulticastLocators 这个函数 会把ip地址设置为 239.255.0.1,并设置端口号,将这个locator 放入m_att.builtin.metatrafficMulticastLocatorList
-
调用NetworkFactory的NormalizeLocators 对 m_att.builtin.metatrafficMulticastLocatorList 进行 处理,遍历注册的Transport,调用Transport 的NormalizeLocator方法
-
Transport 的NormalizeLocator方法 对于多播地址,这儿并没有做处理,如果ip地址是 0.0.0.0,那么就用本地获取的地址(wifi,p2p地址等,通过白名单过滤)替换0.0.0.0,端口号和0.0.0.0的locator 一样,如果ip地址不是0,不做处理
-
NetworkFactory的getDefaultMetatrafficUnicastLocators 调用UDPv4Transport的getDefaultMetatrafficUnicastLocators
-
UDPv4Transport的getDefaultMetatrafficUnicastLocators 这个函数 会把ip地址设置为 0.0.0.0,并设置端口号,将这个locator 放入m_att.builtin.metatrafficUnicastLocatorList
-
调用NetworkFactory的NormalizeLocators 对 m_att.builtin.metatrafficUnicastLocatorList 进行 处理,遍历注册的Transport,调用Transport 的NormalizeLocator方法
-
与步骤5 类似
-
调用NetworkFactory的configureInitialPeerLocator,对builtin.initialPeersList做配置
-
···
-
m_att.defaultUnicastLocatorList为空 并且 m_att.defaultMulticastLocatorList为空
-
调用RTPSParticipantImpl::get_default_unicast_locators
···
这块大体分为3个部分:
-
1.代码112-148行,builtin.metatrafficMulticastLocatorList,builtin多播默认地址和端口号的配置,
builtin.metatrafficUnicastLocatorList,builtin单播默认地址,端口号
-
2.代码152-167行,initialpeer 的处理
-
3.代码178-216行,defaultUnicastLocatorList单播默认的地址,端口号的配置
步骤1-9就是我们刚才说的第一部分的内容 见5.3
步骤10是第二部分的内容 见5.4
步骤11-15是第三部分的内容 见5.5
上面这些主要是设置了m_att.builtin.metatrafficMulticastLocatorList ,m_att.builtin.metatrafficUnicastLocatorList,m_att.defaultUnicastLocatorList ,builtin.initialPeersList这4个locatorList
这里面的LocatorList看起来比较类似,功能是不一样的,builtin是内置的意思,所以m_att.builtin.metatrafficMulticastLocatorList ,m_att.builtin.metatrafficUnicastLocatorList时表示内置的多播和单播地址。内置表示,基本上所有的dds 都会配置的部分。
m_att.defaultUnicastLocatorList表示用户的单播地址。用户对应的是rtps层之上的dds 层,他们是rtps层的使用者。
同时 这3个locatorlist对应的 端口号是不一样的。
我们看到这里面不是对称的,缺少了对m_att.defaultMulticastLocatorList 默认配置,只是因为在dds层,大多数情况下是不使用多播进行通信。多播在发现阶段用的比较多,在发现之后一般使用单播,1对1通信。
builtin.initialPeersList是远端participant的ip地址和端口号。后面详细介绍。
5.3 配置builtin的单播,多播ip地址和端口号
第一部分代码112-148行,配置多播地址端口号,单播地址端口号
builtin.metatrafficMulticastLocatorList 和 builtin.metatrafficUnicastLocatorList 都为空就配置默认的locator,就是默认的本地ip和端口号。
步骤1:
void RTPSParticipantImpl::get_default_metatraffic_locators()
{
uint32_t metatraffic_multicast_port = m_att.port.getMulticastPort(domain_id_);
uint32_t metatraffic_unicast_port = m_att.port.getUnicastPort(domain_id_,
static_cast<uint32_t>(m_att.participantID));
// 239.0.0.1
m_network_Factory.getDefaultMetatrafficMulticastLocators(m_att.builtin.metatrafficMulticastLocatorList,
metatraffic_multicast_port);
m_network_Factory.NormalizeLocators(m_att.builtin.metatrafficMulticastLocatorList);
// 0.0.0.0
m_network_Factory.getDefaultMetatrafficUnicastLocators(m_att.builtin.metatrafficUnicastLocatorList,
metatraffic_unicast_port);
// 替换成了本地地址,通过白名单过滤等方式,加入符合要求的本地地址
m_network_Factory.NormalizeLocators(m_att.builtin.metatrafficUnicastLocatorList);
}
主要干了这几件事情:
-
1.通过调用NetworkFactory::getDefaultMetatrafficMulticastLocators获取builtin的MetatrafficMulticastLocators,就是本地的多播地址对应的locator,locator里面主要是ip地址和端口号
在这儿默认的多播ipv4地址是239.255.0.1,步骤2,步骤3
-
以builtin的MetatrafficMulticastLocators为参数调用NormalizeLocators 步骤4,步骤5
-
通过调用NetworkFactory::getDefaultMetatrafficUnicastLocators获取builtin的MetatrafficUnicastLocators,就是本地单播地址对应的locator,locator主要是ip地址和端口号 步骤6,步骤7
-
以MetatrafficUnicastLocators为参数调用NormalizeLocators 步骤8,步骤9
我们先看一下步骤2:
5.3.1获取builtin的MetatrafficMulticastLocators
bool NetworkFactory::getDefaultMetatrafficMulticastLocators(
LocatorList_t& locators,
uint32_t metatraffic_multicast_port) const
{
bool result = false;
TransportInterface* shm_transport = nullptr;
for (auto& transport : mRegisteredTransports)
{
// For better fault-tolerance reasons, SHM multicast metatraffic is avoided if it is already provided
// by another transport
if (transport->kind() != LOCATOR_KIND_SHM)
{
result |= transport->getDefaultMetatrafficMulticastLocators(locators, metatraffic_multicast_port);
}
else
{
shm_transport = transport.get();
}
}
if (locators.size() == 0 && shm_transport)
{
result |= shm_transport->getDefaultMetatrafficMulticastLocators(locators, metatraffic_multicast_port);
}
return result;
}
这个函数的意思是根据不同的transport设置不同的ip地址和端口号,通过每个transport调用getDefaultMetatrafficMulticastLocators,获取默认的多播地址。
一共有这3大类transport,udptransport(v4,v6),tcptransport(v4,v6),shmtransport,
我们现在以udpv4transport举例 (步骤3),其他的类似。
对于udpv4transport来说 就是239.255.0.1。
步骤3 UDPv4Transport::getDefaultMetatrafficMulticastLocators
bool UDPv4Transport::getDefaultMetatrafficMulticastLocators(
LocatorList& locators,
uint32_t metatraffic_multicast_port) const
{
Locator locator;
locator.kind = LOCATOR_KIND_UDPv4;
locator.port = static_cast<uint16_t>(metatraffic_multicast_port);
// 设置ip地址 239.255.0.1
IPLocator::setIPv4(locator, DEFAULT_METATRAFFIC_MULTICAST_ADDRESS);
// 没有重复的就加入,(是否相等就是看地址,port 是否一样)
locators.push_back(locator);
return true;
}
UDPv4Transport,获取MulticastLocator,设置ip地址为 239.255.0.1,同时设置端口号。
这样就获取到了MulticastLocator,多播的locator
有了locator 之后,需要调用NormalizeLocators 就是步骤4。
5.3.2 NormalizeLocators
步骤4 NetworkFactory::NormalizeLocators
NormalizeLocators 字面意思就是正常化locator,为什么需要正常化locator?
void NetworkFactory::NormalizeLocators(
LocatorList_t& locators)
{
LocatorList_t normalizedLocators;
std::for_each(locators.begin(), locators.end(), [&](Locator_t& loc)
{
bool normalized = false;
for (auto& transport : mRegisteredTransports)
{
// Check if the locator is supported and filter unicast locators.
// 三个判断条件,
// transport->IsLocatorSupported 判断kind是否一致
// IPLocator::isMulticast 是否是多播
// transport->is_locator_allowed 是否允许(多播允许,ip地址在白名单允许,ip地址为(0.0.0.0)允许)
if (transport->IsLocatorSupported(loc) &&
(IPLocator::isMulticast(loc) ||
transport->is_locator_allowed(loc)))
{
// First found transport that supports it, this will normalize the locator.
// 将transport->NormalizeLocator返回的list ,存入到normalizedLocators中
normalizedLocators.push_back(transport->NormalizeLocator(loc));
normalized = true;
}
}
if (!normalized)
{
normalizedLocators.push_back(loc);
}
});
locators.swap(normalizedLocators);
}
//将locator ,正常化
这个函数主要是判断一下locator
- a.是否是这个transport 支持的locator
- b.是否是多播locator
- c.是否is_locator_allowed,(多播允许,ip地址在白名单允许,ip地址为0.0.0.0允许)
如果locator是多播的locator,则不用过多处理,直接存入队列返回,
如果locator如果单播地址是(0.0.0.0),则需要获取本地IP地址 和白名单的交集,存入locatorlist中,为什么会这样处理,
对于0.0.0.0地址的监听,所有远端发给本地ip地址的消息都会传给0.0.0.0地址。
而对于应用程序来说,肯定有对于不同ip地址的处理的需求,那么对于0.0.0.0的处理不能简单的笼统的监听,需要对本机地址分别处理。
step a是否是这个transport 支持的locator
//判断一下kind是否一致
bool UDPTransportInterface::IsLocatorSupported(
const Locator& locator) const
{
return locator.kind == transport_kind_;
}
locator 的kind 和transport_kind_是否一致
step c是否is_locator_allowed
bool UDPv4Transport::is_locator_allowed(
const Locator& locator) const
{
if (!IsLocatorSupported(locator))
{
return false;
}
//白名单为空 或者是多播
if (interface_whitelist_.empty() || IPLocator::isMulticast(locator))
{
return true;
}
return is_interface_allowed(IPLocator::toIPv4string(locator));
}
interface_whitelist_ 为空 ,locator是多播返回 true
否则调用UDPv4Transport::is_interface_allowed
bool UDPv4Transport::is_interface_allowed(
const std::string& interface) const
{
return is_interface_allowed(asio::ip::address_v4::from_string(interface));
}
bool UDPv4Transport::is_interface_allowed(
const ip::address_v4& ip) const
{
//白名单为空
if (interface_whitelist_.empty())
{
return true;
}
//ip地址为0
if (ip == ip::address_v4::any())
{
return true;
}
//在白名单中
return find(interface_whitelist_.begin(), interface_whitelist_.end(), ip) != interface_whitelist_.end();
}
上面函数就是判断一下ip地址是否是被允许的
白名单为空,肯定是允许的
IP地址为(0.0.0.0)是允许的
ip地址在interface_whitelist_ 中也是允许的
总结一下步骤b 多播允许,ip地址在白名单允许(interface_whitelist_),ip地址为0.0.0.0允许,白名单为空允许
步骤5:
LocatorList UDPv4Transport::NormalizeLocator(
const Locator& locator)
{
LocatorList list;
// 如果是0.0.0.0的地址,那么获取本地地址
// 使用本地地址,端口号 与 之前一致
if (IPLocator::isAny(locator))
{
std::vector<IPFinder::info_IP> locNames;
get_ipv4s(locNames);
for (const auto& infoIP : locNames)
{
//遍历本地地址
auto ip = asio::ip::address_v4::from_string(infoIP.name);
// 这个地址是否被允许
if (is_interface_allowed(ip))
{
// port 和 kind 都不变
Locator newloc(locator);
// 地址是新地址
IPLocator::setIPv4(newloc, infoIP.locator);
list.push_back(newloc);
}
}
//没有本地地址,存入回环地址
if (list.empty())
{
Locator newloc(locator);
IPLocator::setIPv4(newloc, "127.0.0.1");
list.push_back(newloc);
}
}
else
{
list.push_back(locator);
}
return list;
}
这个函数针对 多播地址(239.255.0.1)和单播地址(0.0.0.0) 分别处理
如果ip地址是0.0.0.0, 会获取本地地址,然后经过 interface_whitelist_ 过滤,存入一个队列,返回队列
如果ip地址是其他,返回这个存有这个locator的队列
5.3.3获取builtin的getDefaultMetatrafficUnicastLocators
步骤6
bool NetworkFactory::getDefaultMetatrafficUnicastLocators(
LocatorList_t& locators,
uint32_t metatraffic_unicast_port) const
{
bool result = false;
for (auto& transport : mRegisteredTransports)
{
result |= transport->getDefaultMetatrafficUnicastLocators(locators, metatraffic_unicast_port);
}
return result;
}
//遍历注册的Transport,调用transport的方法getDefaultMetatrafficUnicastLocators,获取locators,同时设置port 端口号
步骤7
bool UDPv4Transport::getDefaultMetatrafficUnicastLocators(
LocatorList& locators,
uint32_t metatraffic_unicast_port) const
{
Locator locator;
locator.kind = LOCATOR_KIND_UDPv4;
locator.port = static_cast<uint16_t>(metatraffic_unicast_port);
//设置为0.0.0.0
locator.set_Invalid_Address();
locators.push_back(locator);
return true;
}
//获取默认的单播地址,就是0.0.0.0
步骤8,9 可以参看前面步骤4
5.4builtin.initialPeersList的处理
第二部分 代码152-167行,initialpeer 的处理。 这里介绍一个概念 initialPeers:
initialPeers 就是在pdp发现阶段,除了监听多播外,还可以配置监听单播消息,initialPeers里面存放的就是远端的ip地址和端口号。
可以直接使用这个initialPeers 里的ip。我们在pdp发现阶段,通过监听多播消息之后能够知道有哪些单播的ip地址加入到了fastdds 的网络,那么initialPeers里面的ip地址,就不用通过多播监听,直接能被本地程序知道的远端ip地址和端口号。
这个可以在初始化阶段进行配置。
builtin.initialPeersList 为空,就将builtin.initialPeersList 赋值为 builtin.MetatrafficMulticastLocators
不为空就调用configureInitialPeerLocator 对builtin.initialPeersList 做配置
这就是步骤10
bool NetworkFactory::configureInitialPeerLocator(
uint32_t domain_id,
Locator_t& locator,
RTPSParticipantAttributes& m_att) const
{
bool result = false;
for (auto& transport : mRegisteredTransports)
{
if (transport->IsLocatorSupported(locator))
{
result |= transport->configureInitialPeerLocator(locator, m_att.port, domain_id,
m_att.builtin.initialPeersList);
}
}
return result;
}
这个主要是调用transport的configureInitialPeerLocator函数
UDPTransportInterface 是 UDPv4Transport的父类,如果是UDPv4Transport的话,最终会调用
UDPTransportInterface的configureInitialPeerLocator,配置builtin.initialPeersList。这个会在后面用到。
5.5defaultUnicastLocatorList的ip地址和端口号配置
步骤11:
void RTPSParticipantImpl::get_default_unicast_locators()
{
m_network_Factory.getDefaultUnicastLocators(domain_id_, m_att.defaultUnicastLocatorList, m_att);
m_network_Factory.NormalizeLocators(m_att.defaultUnicastLocatorList);
}
这里面先NetworkFactory 的getDefaultUnicastLocators,
步骤12
bool NetworkFactory::getDefaultUnicastLocators(
uint32_t domain_id,
LocatorList_t& locators,
const RTPSParticipantAttributes& m_att) const
{
bool result = false;
for (auto& transport : mRegisteredTransports)
{
result |= transport->getDefaultUnicastLocators(locators, calculate_well_known_port(domain_id, m_att, false));
}
return result;
}
最终调用到Transport的getDefaultUnicastLocators,我们以UDPv4Transport举例。
步骤13
bool UDPv4Transport::getDefaultUnicastLocators(
LocatorList& locators,
uint32_t unicast_port) const
{
Locator locator;
locator.kind = LOCATOR_KIND_UDPv4;
locator.set_Invalid_Address();
fillUnicastLocator(locator, unicast_port);
locators.push_back(locator);
return true;
}
将ip地址设置为0,
步骤14:调用NormalizeLocators
获取本地ip地址替换ip地址为(0.0.0.0)的locator
参看 步骤4,5,8,9
这里面
defaultMulticastLocatorList 如果没有配置的话,不会有默认配置。
builtin.metatrafficMulticastLocatorList 、 m_att.builtin.metatrafficUnicastLocatorList 对应的是就是说在PDP、EDP、WLP的reader和writer(builtin的writer ,reader)。就是说在builtin的writer ,reader有默认的多播地址和单播地址。
defaultMulticastLocatorLis、defaultUnicastLocatorList 对应的就是应用层writer和reader。我们在第一篇文章中例子程序中创建的就是应用层的writer和reader。
所以buildin的reader和writer在应用层是无法被感知的。
对于用户application的writer ,reader只有默认的单播地址。大多数情况下,用户application都是采用单播的形式来发送消息。
5.6类图
转载自:https://juejin.cn/post/7385445245218619428