车载消息中间件FastDDS 源码解析(十)发送第一条PDP消息(上)

799 阅读14分钟

车载消息中间件FastDDS 源码解析(一)FastDDS 介绍和使用

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

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

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

车载消息中间件FastDDS 源码解析(五)BuiltinProtocols(上)

车载消息中间件FastDDS 源码解析(六)BuiltinProtocols(中)EDP

车载消息中间件FastDDS 源码解析(七)BuiltinProtocols(下)WLP&TypeLookupManager

车载消息中间件FastDDS 源码解析(八)TimedEvent

车载消息中间件FastDDS 源码解析(九)Message

车载消息中间件FastDDS 源码解析(十)PDP启动

在前面几篇中我们介绍了RTPSParticipantImpl的组成部分,现在我们看一下这个RTPSParticipantImpl是如何运转的。

1.PDP发现机制的启动

在第二篇 车载消息中间件FastDDS 源码解析(二)RtpsParticipant的创建(上) 1.2节时序图的第5步中,调用到了RTPSParticipantImpl::enable。

这个就是启动发现机制。就是在fastdds中所有的组件都构建完毕后,启动了fastdds。

1.1时序图

sequenceDiagram
participant RTPSParticipantImpl
		participant BuiltinProtocols
		participant PDP
		
		RTPSParticipantImpl ->> RTPSParticipantImpl: 1.enable
		RTPSParticipantImpl ->> BuiltinProtocols: 2.enable
		BuiltinProtocols ->> PDP: 3.enable
		BuiltinProtocols ->> PDPSimple: 4.announceParticipantState
		PDPSimple ->> PDP: 5.announceParticipantState
		BuiltinProtocols ->> PDP:6.resetParticipantAnnouncement
		PDP ->> RTPSParticipantImpl:7.enableReader
		
  1. RTPSParticipantImpl::enable 主要干了两件事

a.调用了BuiltinProtocols的enable函数

b.Messagereceiver 与Receiverresource关联,Receiverresource会不断监听socket,如果有收到消息,就交给Messagereceiver来处理

  1. BuiltinProtocols的enable函数 主要是调用了

a.PDP::enable函数 见3

b.PDPSimple的announceParticipantState函数 见4

c.resetParticipantAnnouncement 这个函数是 见5

  1. PDP::enable函数 主要是初始化了两个TimedEvent: lease_duration_event 和 resend_participant_info_event_

lease_duration_event 会周期性的check 这个remote_participant 是否还存在,不存在的话,需要把remote_participant移除

resend_participant_info_event_ 看名字就是知道这个event 的功能,就是发送pdp消息

  1. PDPSimple::announceParticipantState函数调用

a.PDP::announceParticipantState函数 如果是true,表示会新建一个change 放入到history中

b.StatelessWriter的unsent_changes_reset 如果没有新change,就发送history中之前的change

这个函数调用flow_controller*->add_new_sample 通过flow_controller* 发送出去

  1. PDP::announceParticipantState函数 主要干了2件事情

a.创建并初始化一个change

将Participant的信息写入change

b.将change放入到WriterHistory中去

  1. PDP:resetParticipantAnnouncement 将resend_participant_info_event_ 启动

  2. 最终调用RTPSParticipantImpl:enbaleReader,将messagereceiver与endpoint(reader 或者writer) 关联

1.2源码解析

在RTPSParticipantImpl初始化完毕后,我们调用RTPSParticipantImpl::enable,这个enable 函数主要负责participant启动,往外发pdp消息,同时接收pdp消息。

步骤1:

 void RTPSParticipantImpl::enable()
 {
     mp_builtinProtocols->enable();
 ​
     //Start reception
     for (auto& receiver : m_receiverResourcelist)
    {
         receiver.Receiver->RegisterReceiver(receiver.mp_receiver);
    }
 }

这个函数主要干了2件事:

1.调用BuiltinProtocols::enable

2.messagereceiver 与receiverresource 关联在一起

步骤2:

 void BuiltinProtocols::enable()
 {
     if (nullptr != mp_PDP)
    {
         mp_PDP->enable();
         mp_PDP->announceParticipantState(true);
         mp_PDP->resetParticipantAnnouncement();
    }
 }

这里面调用了3个函数,下面分别介绍

1.PDP的函数 enbale

2.PDP的函数announceParticipantState

3.PDP的函数resetParticipantAnnouncement

步骤3:

 bool PDP::enable()
 {
     // It is safe to call enable() on already enable PDPs
     if (enabled_)
    {
         return true;
    }
 ​
     // Create lease events on already created proxy data objects
     for (ParticipantProxyData* pool_item : participant_proxies_pool_)
    {
         pool_item->lease_duration_event = new TimedEvent(mp_RTPSParticipant->getEventResource(),
                        [this, pool_item]() -> bool
                        {
                             check_remote_participant_liveliness(pool_item);
                             return false;
                        }, 0.0);
    }
 ​
     resend_participant_info_event_ = new TimedEvent(mp_RTPSParticipant->getEventResource(),
                    [&]() -> bool
                    {
                         announceParticipantState(false);
                         set_next_announcement_interval();
                         return true;
                    },
                     0);
 ​
     set_initial_announcement_interval();
 ​
     enabled_.store(true);
     // Notify "self-discovery"
     getRTPSParticipant()->on_entity_discovery(mp_RTPSParticipant->getGuid(),
             get_participant_proxy_data(mp_RTPSParticipant->getGuid().guidPrefix)->m_properties);
 ​
     return builtin_endpoints_->enable_pdp_readers(mp_RTPSParticipant);
 }

这个函数主要干了3件事:

这里面有个两个TimedEvent:

1.创建了一个TimedEvent :lease_duration_event

lease_duration_event 会周期性的check 这个remote_participant 是否还存在,不存在的话,不存在就把remote_participant移除

2.resend_participant_info_event_

resend_participant_info_event_ 看名字就是知道这个event 的功能,就是重新发送pdp消息,里面主要调用了2个函数

announceParticipantState(false); 如果是false 并不会将新change加入到history中,会发送在history 中的change。

set_next_announcement_interval();设置下一次触发resend_participant_info_event_的时间间隔

3.PDP::set_initial_announcement_interval

设置resend_participant_info_event_的时间间隔

4.builtin_endpoints_->enable_pdp_readers

这个函数主要就是启动reader,能够接收pdp消息

最终调用的是RTPSParticipantImpl::enableReader

也就是步骤7

 void PDP::set_initial_announcement_interval()
 {
     if ((initial_announcements_.count > 0) && (initial_announcements_.period <= c_TimeZero))
    {
         // Force a small interval (1ms) between initial announcements
         EPROSIMA_LOG_WARNING(RTPS_PDP, "Initial announcement period is not strictly positive. Changing to 1ms.");
         initial_announcements_.period = { 01000000 };
    }
     set_next_announcement_interval();
 }

设置initial_announcements_.period 为100ms

set_next_announcement_interval 这个是设置发送下一次的消息的时间

 void PDP::set_next_announcement_interval()
 {
     if (initial_announcements_.count > 0)
    {
         --initial_announcements_.count;
         resend_participant_info_event_->update_interval(initial_announcements_.period);
    }
     else
    {
         resend_participant_info_event_->update_interval(m_discovery.discovery_config.leaseDuration_announcementperiod);
    }
 }

initial_announcements_.count 默认值是5

initial_announcements_.period 默认值是100ms

在初始化的时候,发送pdp消息,默认间隔是100ms 发送5次

5次发送完毕之后,发送间隔是leaseDuration_announcementperiod,默认设置为3s

这些都是可配置的,我们在使用dds的时候可以配置这些参数。

总结一下就是发送pdp消息,在初始化阶段,默认发送5次,每次间隔100ms,

(其实是6次,在初始化的时候,先发送一条,然后每隔100ms 发送一次),发送完毕之后,

每隔3s发送一次pdp消息。

我们看一下tcpdump抓的包

image-20231203174200070.png

在一 处标识的是发送的时间,我们可以清楚的看到,第一次发送时间是0.106834s,第二次是0.205335s,发送消息的时间间隔约为0.1s大概是 100ms,因为发送的时间在网络层会有一些误差,所以并不是完美的100ms。

一共发了6条pdp消息,第一条是初始化阶段开始的时候发送的pdp消息,之后5条是通过timeevent发送的初始化消息,每隔100ms发送一次,发送了5次。

二处显示的消息类型是RTPS消息。

三处表明这个消息的大概内容。在这里DATA(p)表示这是一个pdp 的发现消息。

步骤4:

 void PDPSimple::announceParticipantState(
         bool new_change,
         bool dispose,
         WriteParams& wp)
 {
     if (enabled_)
    {
         auto endpoints = static_cast<fastdds::rtps::SimplePDPEndpoints*>(builtin_endpoints_.get());
         StatelessWriter& writer = *(endpoints->writer.writer_);
         WriterHistory& history = *(endpoints->writer.history_);
 ​
         PDP::announceParticipantState(writer, history, new_change, dispose, wp);
 ​
         if (!(dispose || new_change))
        {
             //将history中的消息都发送出去
             writer.unsent_changes_reset();
        }
    }
 }

这个函数主要干了2件事情:

1.PDP::announceParticipantState

这就是步骤5,这个我们在下面详细讨论

2.StatelessWriter的unsent_changes_reset函数

也就是步骤6:

bool new_change, 表示是否需要创建一个new change,false表示不创建,使用history中的消息。true表示重新创建。

步骤6

 void PDP::resetParticipantAnnouncement()
 {
     if (resend_participant_info_event_)
    {
         resend_participant_info_event_->restart_timer();
    }
 }

resend_participant_info_event_ 重新开始

步骤7:

//builtin_endpoints_->enable_pdp_readers 最终调用的是RTPSParticipantImpl::enableReader

 bool RTPSParticipantImpl::enableReader(
         RTPSReader* reader)
 {
     if (!assignEndpointListenResources(reader))
    {
         return false;
    }
     return true;
 }

assignEndpointListenResources 这个函数在builtinprotocols这个章节介绍过。

就是将messagereceiver与endpoint(reader 或者writer) 关联

2.PDPSimple::announceParticipantState

这个函数主要就是发送pdp消息

2.1时序图

sequenceDiagram
participant PDP
		participant PDPSimple
		participant RTPSWriter
		participant WriterHistory
		participant StatelessWriter
		participant FlowControllerImpl
		
		PDPSimple ->> PDP: 1.announceParticipantState
		PDP ->> RTPSWriter: 2.new_change
		RTPSWriter ->> WriterHistory: 3.add_change
		WriterHistory ->> WriterHistory: 4.add_change_
		WriterHistory ->> WriterHistory: 5.prepare_and_add_change
		WriterHistory ->> WriterHistory: 6.notify_writer
		WriterHistory ->> StatelessWriter: 7.unsent_change_added_to_history
		StatelessWriter ->> FlowControllerImpl: 8.add_new_sample
		PDPSimple ->> StatelessWriter:9.unsent_changes_reset
		StatelessWriter ->> FlowControllerImpl: 10.add_new_sample
		

PDPSimple::announceParticipantState函数调用

a.PDP::announceParticipantState函数

b.StatelessWriter的unsent_changes_reset 如果没有新change,就发送history中之前的change 见9

  1. PDP::announceParticipantState函数 主要干了2件事情

    a.创建并初始化一个change 见6 将Participant的信息写入change

    b.将change放入到WriterHistory中去 见3

  2. RTPSWriter::new_change 创建了一个change,并配置相关参数

  3. WriterHistory::add_change 调用WriterHistory::add_change_

  4. WriterHistory::add_change_干了2件事

    a.调用WriterHistory::prepare_and_add_change 见5

    b.调用WriterHistory::notify_writer 见6

  5. WriterHistory::prepare_and_add_change 给每个新change 一个sequenceNumber,然后将change存入

  6. WriterHistory::notify_writer 调用statelesswriter 的 unsent_change_added_to_history函数

  7. statelesswriter 的 unsent_change_added_to_history函数 调用FlowControllerimpl的add_new_sample

  8. FlowControllerimpl的add_new_sample,将change 交给FlowControllerimpl 来发送出去

  9. StatelessWriter的unsent_changes_reset, 就是发送history中的change

  10. FlowControllerimpl的add_new_sample,将change 交给FlowControllerimpl 来发送出去

2.2源码解析

步骤1:

 void PDP::announceParticipantState(
         RTPSWriter& writer,
         WriterHistory& history,
         bool new_change,
         bool dispose,
         WriteParams& wparams)
 {
     if (enabled_)
    {
         // EPROSIMA_LOG_INFO(RTPS_PDP, "Announcing RTPSParticipant State (new change: " << new_change << ")");
         CacheChange_t* change = nullptr;
 ​
         if (!dispose)
        {
           //如果本地participant 的信息,有变化,则更新本地的ParticipantProxyData
             if (m_hasChangedLocalPDP.exchange(false) || new_change)
            {
                 this->mp_mutex->lock();
                 ParticipantProxyData* local_participant_data = getLocalParticipantProxyData();
                 InstanceHandle_t key = local_participant_data->m_key;
                 ParticipantProxyData proxy_data_copy(*local_participant_data);
                 this->mp_mutex->unlock();
 ​
                 if (history.getHistorySize() > 0)
                {
                     //本地的ParticipantProxyData,都是在history的最上面
                     history.remove_min_change();
                }
                 uint32_t cdr_size = proxy_data_copy.get_serialized_size(true);
                 //新建一个change
                 change = writer.new_change(
                    [cdr_size]() -> uint32_t
                    {
                         return cdr_size;
                    },
                     ALIVE, key);
 ​
                 if (change != nullptr)
                {
                     //change内存 和 aux_msg的内存相关联
                     CDRMessage_t aux_msg(change->serializedPayload);
 ​
 #if __BIG_ENDIAN__
                     change->serializedPayload.encapsulation = (uint16_t)PL_CDR_BE;
                     aux_msg.msg_endian = BIGEND;
 #else
                     change->serializedPayload.encapsulation = (uint16_t)PL_CDR_LE;
                     aux_msg.msg_endian =  LITTLEEND;
 #endif // if __BIG_ENDIAN__
 ​
                   //ParticipantProxyData的信息写入CDRMessage_t 的对象aux_msg
                     if (proxy_data_copy.writeToCDRMessage(&aux_msg, true))
                    {
                         change->serializedPayload.length = (uint16_t)aux_msg.length;
 //将change 放入history 中
                         history.add_change(change, wparams);
                    }
                    ······
                }
            }
 ​
        }
         else
        {
             this->mp_mutex->lock();
             ParticipantProxyData proxy_data_copy(*getLocalParticipantProxyData());
             this->mp_mutex->unlock();
 ​
             if (history.getHistorySize() > 0)
            {
                 history.remove_min_change();
            }
             uint32_t cdr_size = proxy_data_copy.get_serialized_size(true);
             change = writer.new_change([cdr_size]() -> uint32_t
                            {
                                 return cdr_size;
                            },
                             NOT_ALIVE_DISPOSED_UNREGISTERED, getLocalParticipantProxyData()->m_key);
 ​
             if (change != nullptr)
            {
                 CDRMessage_t aux_msg(change->serializedPayload);
 ​
 #if __BIG_ENDIAN__
                 change->serializedPayload.encapsulation = (uint16_t)PL_CDR_BE;
                 aux_msg.msg_endian = BIGEND;
 #else
                 change->serializedPayload.encapsulation = (uint16_t)PL_CDR_LE;
                 aux_msg.msg_endian =  LITTLEEND;
 #endif // if __BIG_ENDIAN__
 ​
                 if (proxy_data_copy.writeToCDRMessage(&aux_msg, true))
                {
                     change->serializedPayload.length = (uint16_t)aux_msg.length;
 ​
                     history.add_change(change, wparams);
                }
                 else
                {
                     EPROSIMA_LOG_ERROR(RTPS_PDP, "Cannot serialize ParticipantProxyData.");
                }
            }
        }
    }
 }

// 创建change,初始化,并存入history中去

主要干了2件事

1.初始化CacheChange_t 对象( 步骤2)

2.将change 存入history中去(步骤3)

CDRMessage_t中的buffer 和 change->serializedPayload中的data 地址是一样的。

change 的类型有4种

ALIVE,

NOT_ALIVE_DISPOSED, //就是writer告知reader,我要下线了,你把这个消息处理一下

NOT_ALIVE_UNREGISTERED, //就是writer告知reader,我要把这个instance 注销掉

NOT_ALIVE_DISPOSED_UNREGISTERED//就是writer告知reader,我要下线你把消息处理一下,把这个instance 注销掉

步骤2:

 CacheChange_t* RTPSWriter::new_change(
         const std::function<uint32_t()>& dataCdrSerializedSize,
         ChangeKind_t changeKind,
         InstanceHandle_t handle)
 {
     EPROSIMA_LOG_INFO(RTPS_WRITER, "Creating new change");
 ​
     std::lock_guard<RecursiveTimedMutex> guard(mp_mutex);
     CacheChange_t* reserved_change = nullptr;
     if (!change_pool_->reserve_cache(reserved_change))
    ······
 ​
     uint32_t payload_size = fixed_payload_size_ ? fixed_payload_size_ : dataCdrSerializedSize();
     if (!payload_pool_->get_payload(payload_size, *reserved_change))
    ······
 ​
     reserved_change->kind = changeKind;
     if (m_att.topicKind == WITH_KEY && !handle.isDefined())
    {
         EPROSIMA_LOG_WARNING(RTPS_WRITER, "Changes in KEYED Writers need a valid instanceHandle");
    }
     reserved_change->instanceHandle = handle;
     reserved_change->writerGUID = m_guid;
     reserved_change->writer_info.previous = nullptr;
     reserved_change->writer_info.next = nullptr;
     reserved_change->writer_info.num_sent_submessages = 0;
     return reserved_change;
 }

这个函数主要主要是从缓存中获取一个change,同时初始化change

change的缓存:

change_pool_ 和 payload_pool_这儿有两个缓存池

CacheChange_t 中有一个参数 SerializedPayload_t serializedPayload 这个对象是payload_pool_ 这个缓存池中缓存的对象

两个缓存池是关联的

change_pool是change的缓存池,而change中的serializedPayload是payload_pool_这个缓存池分配的。

缓存的类型有以下几种

PREALLOCATED_MEMORY_MODE, 提前分配好内存,那个内存按照最大值分配,这种分配方式,内存占用最高,但是需要分配的次数最少

PREALLOCATED_WITH_REALLOC_MEMORY_MODE,提前分配好内存,内存按照平均水平分配,如果来了一个消息,内存值大于之前分配的内存值,需要重新分配,这种分配方式,会比第一中需要的内存少点,但是分配的次数会增多

DYNAMIC_RESERVE_MEMORY_MODE, 动态分配内存,来一个重新分配一个,最少的内存占用,最多的内存分配次数

DYNAMIC_REUSABLE_MEMORY_MODE 动态分配内存,和上一个策略类似,会复用之前用过的内存,比上一个策略内存分配少一点,内存使用多一点

步骤3:

 bool WriterHistory::add_change(
         CacheChange_t* a_change,
         WriteParams& wparams)
 {
     return add_change_(a_change, wparams);
 }

ChangeKind_t 这个是RTPS消息在History中的存储的数据结构。

这个函数主要调用了add_change_ 函数 (步骤4)

将change 加入的history中

WriterHistory存放了所有需要发送的消息

步骤4:

 bool WriterHistory::add_change_(
         CacheChange_t* a_change,
         WriteParams& wparams,
         std::chrono::time_point<std::chrono::steady_clock> max_blocking_time)
 {
     if (mp_writer == nullptr || mp_mutex == nullptr)
    {
         EPROSIMA_LOG_ERROR(RTPS_WRITER_HISTORY,
                 "You need to create a Writer with this History before adding any changes");
         return false;
    }
 ​
     std::lock_guard<RecursiveTimedMutex> guard(*mp_mutex);
     if (!prepare_and_add_change(a_change, wparams))
    {
         return false;
    }
 ​
     notify_writer(a_change, max_blocking_time);
 ​
     return true;
 }

主要干了2件事情:

1.prepare_and_add_change

也就是步骤5

2.notify_writer 也就是步骤6

将change 放入 WriterHistory中

notify_writer主要是告知writer

步骤5:

 bool WriterHistory::prepare_and_add_change(
         CacheChange_t* a_change,
         WriteParams& wparams)
 {
     ------
 ​
     ++m_lastCacheChangeSeqNum;
     //每个change有一个唯一的sequenceNumber,是由WriterHistory的m_lastCacheChangeSeqNum 赋值的
     a_change->sequenceNumber = m_lastCacheChangeSeqNum;
     if (wparams.source_timestamp().seconds() < 0)
    {
         Time_t::now(a_change->sourceTimestamp);
    }
     else
    {
         a_change->sourceTimestamp = wparams.source_timestamp();
    }
     a_change->writer_info.num_sent_submessages = 0;
 ​
     a_change->write_params = wparams;
     //Updated sample and related sample identities on the user's write params
     wparams.sample_identity().writer_guid(a_change->writerGUID);
     wparams.sample_identity().sequence_number(a_change->sequenceNumber);
     //这个能够帮助回复的消息 和 请求的消息相关联
     wparams.related_sample_identity(wparams.sample_identity());
     set_fragments(a_change);
 ​
     m_changes.push_back(a_change);
 ​
     if (static_cast<int32_t>(m_changes.size()) == m_att.maximumReservedCaches)
    {
         m_isHistoryFull = true;
    }
 ​
    ······
 ​
     return true;
 }
 ​

这个函数干了2件事

1.设置change的时间戳和sequenceNumber

2.set_fragments 如果change 数据量太大,就将change 分片

先看设置change的时间戳和sequenceNumber

每个change有一个唯一的sequenceNumber,是由WriterHistory的m_lastCacheChangeSeqNum(简称seq) 赋值的,这是change的唯一标志

这个标识很重要,在消息由于某种原因丢失重发的机制中发挥重要作用。我们发送消息的时候,会告知writer,我们要发送的message的seq是多少,我们丢失了消息之后,reader给writer发送心跳消息的时候,需要告诉writer哪个seq的消息丢失了,writer就重发相应的消息,整个机制比较复杂,我们在后面详细介绍。

时间戳的话就是将change 的时间戳设置成现在的系统时间

步骤6:

 void WriterHistory::notify_writer(
         CacheChange_t* a_change,
         const std::chrono::time_point<std::chrono::steady_clock>& max_blocking_time)
 {
     mp_writer->unsent_change_added_to_history(a_change, max_blocking_time);
 }

调用writer的unsent_change_added_to_history函数,其实就是将change 发送出去

步骤7:

 void StatelessWriter::unsent_change_added_to_history(
         CacheChange_t* change,
         const std::chrono::time_point<std::chrono::steady_clock>& max_blocking_time)
 {
     std::lock_guard<RecursiveTimedMutex> guard(mp_mutex);
     auto payload_length = change->serializedPayload.length;
 ​
     if (liveliness_lease_duration_ < c_TimeInfinite)
    {
         // 发送liveness的包
         mp_RTPSParticipant->wlp()->assert_liveliness(
             getGuid(),
             liveliness_kind_,
             liveliness_lease_duration_);
    }
 ​
     // Notify the datasharing readers
     // This also prepares the metadata for late-joiners
     if (is_datasharing_compatible())
    {
         datasharing_delivery(change);
    }
 ​
     // Now for the rest of readers
     if (!fixed_locators_.empty() || getMatchedReadersSize() > 0)
    {
       //只有在fixed_locators_不为空或者getMatchedReadersSize()>0的时候才会调用这个函数
         flow_controller_->add_new_sample(this, change, max_blocking_time);
    }
     else
    {
         EPROSIMA_LOG_INFO(RTPS_WRITER, "No reader to add change.");
         if (mp_listener != nullptr)
        {
             mp_listener->onWriterChangeReceivedByAll(this, change);
        }
    }
 ​
     // Throughput should be notified even if no matches are available
     on_publish_throughput(payload_length);
 }

主要干了这几件事情:

1.wlp()->assert_liveliness 这个在wlp章节介绍详细内容,

主要是改变本地writer 的状态,因为本地writer有了发送动作,表明本地的writer是live的状态

2.当writer 知道WriterHistory中有新的消息

调用datasharing_delivery 就先发送本地的消息,(同进程,跨进程消息)

(我不推荐dds的跨进程通信

并不是dds的跨进程通信写的不好,而是跨进程通信对于dds来说就是一个鸡肋,如果使用跨进程通信,完全没必要使用dds 这么重的方式,写一个纯净的跨进程通信的代码就可以。)

3.flow_controller*->add_new_sample这个函数就是将消息放入flow_controller*中,这个之前介绍过

flow_controller就是控制消息发送的控制类

这个是有条件的就是需要fixed_locators不为空,这个fixed_locators之前说过就是intialPeersList经过处理后的数据

发送跨设备消息

步骤8:

 bool add_new_sample(
             fastrtps::rtps::RTPSWriter* writer,
             fastrtps::rtps::CacheChange_t* change,
             const std::chrono::time_point<std::chrono::steady_clock>& max_blocking_time) override
    {
         return add_new_sample_impl(writer, change, max_blocking_time);
    }
 ​

步骤9:

 void StatelessWriter::unsent_changes_reset()
 {
     std::lock_guard<RecursiveTimedMutex> guard(mp_mutex);
     std::for_each(mp_history->changesBegin(), mp_history->changesEnd(), [&](CacheChange_t* change)
            {
                 flow_controller_->add_new_sample(this, change,
                 std::chrono::steady_clock::now() + std::chrono::hours(24));
            });
 }

将history中的消息通过flow_controller 发送出去

步骤10:

和步骤8 一样

两个发送动作是一样的,就是发送的locator是不一样的

步骤8中发送的其实是已经配对的reader(包括initialpeerlist),步骤10发送的history中的历史消息

在第7步中我们看到干了3件事 1.wlp()->assert_liveliness 2.调用datasharing_delivery 就先发送本地的消息,(同进程,跨进程消息) 3.flow_controller*->add_new_sample 发送跨设备消息 我们下一篇先介绍 发送跨设备消息,然后再介绍发送本地消息和assert_liveliness

车载消息中间件FastDDS 源码解析(一)FastDDS 介绍和使用

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

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

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

车载消息中间件FastDDS 源码解析(五)BuiltinProtocols(上)

车载消息中间件FastDDS 源码解析(六)BuiltinProtocols(中)EDP

车载消息中间件FastDDS 源码解析(七)BuiltinProtocols(下)WLP&TypeLookupManager

车载消息中间件FastDDS 源码解析(八)TimedEvent

车载消息中间件FastDDS 源码解析(九)Message