likes
comments
collection
share

车载消息中间件FastDDS 源码解析(三)RtpsParticipant的创建(中)

作者站长头像
站长
· 阅读数 28

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 的部分内容,如下图所示: 车载消息中间件FastDDS 源码解析(三)RtpsParticipant的创建(中) 这个手机的话有这几个地址

  • 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时序图

RTPSParticipantImplNetworkFactoryUDPv4TransportDescriptorUDPv4Transport1.RegisterTransport2.create_transport3.new UDPv4Transport4.get_ipv4s5.init6.get_ips7.get_ipv4sRTPSParticipantImplNetworkFactoryUDPv4TransportDescriptorUDPv4Transport
  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.参数配置

  2. create_transport 创建了udpv4transport,调用了new udpv4transport

  3. 在new udpv4transport的过程中,如果interfaceWhiteList 不为空,调用了get_ipv4s

  4. get_ipv4s,将获取到的ip地址和interfaceWhiteList的并集 放入interface_whitelist_中

    get_ipv4s获取了本机ip地址(包括回环地址 ipv4 一般是 127.0.0.1)

  5. UDPTransportInterface::init 初始化udpv4transport

    在这个函数中会调用UDPv4Transport的get_ips的函数

  6. UDPv4Transport的get_ips调用UDPv4Transport的get_ipv4s函数

  7. 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件事

  1. TransportDescriptorInterface 调用create_transport,创建transport

  2. 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件事情:

  1. 获取本地的ip地址

  2. 本地 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件事情

  1. 获取本地ip地址
  2. 丢掉ipv6地址

ip.type一般有4种,IP4,IP6,IP4_LOCAL, IP6_LOCAL 4种,IP4_LOCAL就是本地ipv4地址

locator.kind一般分为5种

  1. /// UDP over IPv4 locator kind

    #define LOCATOR_KIND_UDPv4 1

  2. /// UDP over IPv6 locator kind

    #define LOCATOR_KIND_UDPv6 2

  3. /// TCP over IPv4 kind

    #define LOCATOR_KIND_TCPv4 4

  4. /// TCP over IPv6 locator kind

    #define LOCATOR_KIND_TCPv6 8

  5. /// 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类图

RTPSParticipantImpl
+NetworkFactory m_network_Factory
NetworkFactory
+std::vector> mRegisteredTransports
TransportInterface
UDPTransportInterface
UDPV4Transport
+std::vector interface_whitelist_
UDPV6Transport
TCPTransportInterface
TCPV4Transport
TCPV6Transport

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地址类型的一个对象。

RTPSParticipantImplNetworkFactoryUDPv4Transport1.get_default_metatraffic_locators2.getDefaultMetatrafficMulticastLocators3.getDefaultMetatrafficMulticastLocators4.NormalizeLocators5.NormalizeLocator6.getDefaultMetatrafficUnicastLocators7.getDefaultMetatrafficUnicastLocators8.NormalizeLocators9.NormalizeLocator10.configureInitialPeerLocator11.get_default_unicast_locators12.getDefaultUnicastLocators13.getDefaultUnicastLocators14.NormalizeLocators15.NormalizeLocatorRTPSParticipantImplNetworkFactoryUDPv4Transport
  1. 如果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

  2. NetworkFactory的getDefaultMetatrafficMulticastLocators 会调用UDPv4Transport的getDefaultMetatrafficMulticastLocators

  3. UDPv4Transport的getDefaultMetatrafficMulticastLocators 这个函数 会把ip地址设置为 239.255.0.1,并设置端口号,将这个locator 放入m_att.builtin.metatrafficMulticastLocatorList

  4. 调用NetworkFactory的NormalizeLocators 对 m_att.builtin.metatrafficMulticastLocatorList 进行 处理,遍历注册的Transport,调用Transport 的NormalizeLocator方法

  5. Transport 的NormalizeLocator方法 对于多播地址,这儿并没有做处理,如果ip地址是 0.0.0.0,那么就用本地获取的地址(wifi,p2p地址等,通过白名单过滤)替换0.0.0.0,端口号和0.0.0.0的locator 一样,如果ip地址不是0,不做处理

  6. NetworkFactory的getDefaultMetatrafficUnicastLocators 调用UDPv4Transport的getDefaultMetatrafficUnicastLocators

  7. UDPv4Transport的getDefaultMetatrafficUnicastLocators 这个函数 会把ip地址设置为 0.0.0.0,并设置端口号,将这个locator 放入m_att.builtin.metatrafficUnicastLocatorList

  8. 调用NetworkFactory的NormalizeLocators 对 m_att.builtin.metatrafficUnicastLocatorList 进行 处理,遍历注册的Transport,调用Transport 的NormalizeLocator方法

  9. 与步骤5 类似

  10. 调用NetworkFactory的configureInitialPeerLocator,对builtin.initialPeersList做配置

  11. ···

  12. m_att.defaultUnicastLocatorList为空 并且 m_att.defaultMulticastLocatorList为空

  13. 调用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. 1.通过调用NetworkFactory::getDefaultMetatrafficMulticastLocators获取builtin的MetatrafficMulticastLocators,就是本地的多播地址对应的locator,locator里面主要是ip地址和端口号

    在这儿默认的多播ipv4地址是239.255.0.1,步骤2,步骤3

  2. 以builtin的MetatrafficMulticastLocators为参数调用NormalizeLocators 步骤4,步骤5

  3. 通过调用NetworkFactory::getDefaultMetatrafficUnicastLocators获取builtin的MetatrafficUnicastLocators,就是本地单播地址对应的locator,locator主要是ip地址和端口号 步骤6,步骤7

  4. 以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::stringinterfaceconst
 {
     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类图

RTPSParticipantImpl
+NetworkFactory m_network_Factory
+RTPSParticipantAttributes m_att
NetworkFactory
+std::vector> mRegisteredTransports
UDPV4Transport
+std::vector interface_whitelist_
RTPSParticipantAttributes
+BuiltinAttributes builtin
+LocatorList_t defaultUnicastLocatorList
+LocatorList_t defaultMulticastLocatorList
BuiltinAttributes
+LocatorList_t metatrafficUnicastLocatorList
+LocatorList_t metatrafficMulticastLocatorList
转载自:https://juejin.cn/post/7385445245218619428
评论
请登录