RTPS代码阅读记录

RTPS代码阅读记录

PDP包的发送间隔是100毫秒(找到在哪里设置的)

A: void PDP::set_initial_announcement_interval()函数中设置

initial_announcements_.period = { 0, 1000000 };

如果订阅端希望上线后,可以收到发布端之前已经发布的消息,则发布和订阅端必须设置如下Qos

durability().kind = TRANSIENT_LOCAL_DURABILITY_QOS;

reliability().kind = RELIABLE_RELIABILITY_QOS;

此外,发布端可以重发多少已经发布的消息,取决于其设置的history的depth

wqos.history().depth = 2; //history只存2个历史消息,因此最多能发两个订阅端上线前已经发布的消息

但是,如果wqos.history().kind如果设置了KEEP_ALL_HISTORY_QOS,那么发布端会无视上面设置的history().depth,将所有历史消息都补发给订阅端

很多子消息中有count元素,这个count元素的作用是什么?

A: 类似于SequenceNumber,用于非Data子消息,防止对端Reader读到重复数据

Reader依据什么条件判定对端Writer失效了?

A: a.第一种情况是对端的Participant都销毁了,然后本地的PDP发现对端Partipant的PDP报文发送间隔超过了leaseDuration,这个时候就认为对端Participant下线

此时就会进行remove_participant的操作,顺带移除了远端Participant下面所有匹配的endpoint

c++ 复制代码
PDP::check_remote_participant_liveliness    ( // 定时执行检查 )
     PDP::remove_remote_participant
 	    PDPSimple::removeRemoteEndpoints
 	    	StatefulReader::matched_writer_remove 

b.第二种情况是对端的Writer被销毁了 (Participant还在)

  StatefulReader::processDataMsg 
      EDPSimplePUBListener::onNewCacheChangeAdded    (收到的Change的状态不是ALIVE,说明对端下线了)
          PDP::removeWriterProxyData
              EDP::unpairWriterProxy
                  EDP::unpairWriterProxy
                      StatefulReader::matched_writer_remove 

Writer依据什么条件判定对端Reader实效了?

A: 和Reader判定Writer实效的条件基本一致,就是PDP报文超时或者EDP报文中的EDPSImpleSUBListener收到非ALIVE状态的Change标识对端Reader下线了

ReaderQos的History的length设置的过短会不会导致丢失数据?

A: 使用HelloWorld测试程序设置订阅端DataReaderQos的history.length=1,通过publisher端按照1000hz的频率发送数据,测试10分钟,没有丢包

网卡白名单是在哪个配置中设置的?

A: 默认如果我们创建RTPSParticipant的时候,不指定transport,那么这个transport只有一个默认的UDPv4Transport可以使用,这个UDPv4Transport是没有网卡白名单的。

因此,我们首先要指定用户级别的Transport (不是builtin内建的),指定我们需要我们创建的Reader/Writer通过什么传输方式,并且设置走哪些网卡通信

这些设置是在TransportDescriptor上的,并且最终需要配置在Participant上面,

然后我们需要设置Participant的Qos,让RTPSParticipantImpl不要自己用builtin的Transport,而是使用用户创建的Transport

代码如下:

c++ 复制代码
auto transportDescriptor = std::make_shared<UDPv4TransportDescriptor>();   	// 创建用户自己的UDPv4传输类型
transportDescriptor->interfaceWhiteList.emplace_back("192.168.7.22"); 	// 这里的参数可以传网卡名称  // 添加网卡白名单
DomainParticipantQos pqos;      						// ParticipantQos
pqos.transport().user_transports.push_back(transportDescriptor);  		// 将用户自己创建的udptransport添加到user_transports中
pqos.transport().use_builtin_transport = false;    				// 不使用builtin的Transport,使用用户设置在user_transports中的传输方式

ReaderProxy的作用?

A: 在StatefulWriter中代表对端匹配的某个Reader,保存了和该Reader的数据通信状态,主要是CacheChange的状态(发送过的Change的最大SN,对端Reader请求的Change的最大SN以及已经确认接收的Change的集合)

此外,ReaderProxy还提供了对相关Change状态的查询 (例如查询某个Change是否已经不在队列中了) 和更新接口 (状态在ChangeForReaderStatus_t枚举中定义)

当StatefulWriter向Reader发送数据以及处理该Reader收到的心跳包时,都会同步更新ReaderProxy中CacheChange记录的状态 (实际是一个结构体,其中包含了CacheChange的指针)

​ 在ReaderProxy中,CacheChange有如下几种状态:

c++ 复制代码
enum ChangeForReaderStatus_t
{
    UNSENT = 0,                    //!< UNSENT           Writer未发送
    REQUESTED = 1,                 //!< REQUESTED        Reader请求发送
    UNACKNOWLEDGED = 2,            //!< UNACKNOWLEDGED   Reader未接收到
    ACKNOWLEDGED = 3,              //!< ACKNOWLEDGED     Reader已接收到
    UNDERWAY = 4                   //!< UNDERWAY         准备发送中
};

​ ReaderProxy保存了发给对端Reader的CacheChange的状态,Change指针以及序列号,保存在ChangeForReader_t结构体中

c++ 复制代码
class ChangeForReader_t
{
    ChangeForReaderStatus_t status_;    	// 该CacheChange的状态
    SequenceNumber_t seq_num_;      	// CacheChange的序号
    CacheChange_t* change_;    		// 关联的CacheChange
    FragmentNumberSet_t unsent_fragments_;  
}

​ ReaderProxy的主要成员如下:

c++ 复制代码
bool is_active_;  // 标记远端Reader是否还在线,stop了之后is_active_为false
ReaderLocator locator_info_;  // 对端Reader通信地址
DurabilityKind_t durability_kind_;  // 对端Reader的Durability属性(EDP报文中获取到)
bool expects_inline_qos_;  // 是否希望数据报文中携带Qos信息用于对端过滤
bool is_reliable_;  // 标记对端Reader的Reliability属性
bool disable_positive_acks_;  // 标记是否禁止对端Reader主动发送ACKNACK(一般在匹配上后,对端Reader会立刻发送一包ACKNACK子消息)
StatefulWriter* writer_;  // 和该ReaderProxy配对的本端Writer
ResourceLimitedVector<ChangeForReader_t, std::true_type> changes_for_reader_;  // 和该Reader关联的Change集合以及Change的状态
TimedEvent* nack_supression_event_;  // 当收到对端Reader的ACKNACK后,Writer并不是立刻进行补发,而是设置了定时器做补发的动作,因为这个时候还有新的Change在添加到History
				         // 直到添加新Change的动作暂时停止(就是间隔超过了nack_supression_event_设置的定时,nack_supression_event_定时器才启动进行补发)
				         // nack_supression_event_的定时是在创建StatefulWriter的时候通过WriterAttributes传入的(见WriterTimes结构体)
TimedEvent* initial_heartbeat_event_;  // 发送第一包心跳包的定时器(延时器)
std::atomic_bool timers_enabled_;  // 是否开启心跳包和ANCKANC回复定时器(依据是否有远程reliable类型的Reader和该Writer匹配来决定是否开启)
uint32_t last_acknack_count_;  // 最后一次收到的ACKNACK子消息中的count元素的值
uint32_t last_nackfrag_count_;  // 最后一次收到的NACKFrag子消息中的count元素的值
SequenceNumber_t changes_low_mark_;  // WriterHistory中CacheChange中序号最小值
bool active_ = false;  // 当WriterHistory中的Change被发送到ReaderProxy代表的对端Reader时,active_为true(StatefulWriter::deliver_sample_to_network)

​ 其中nack_supression_event_和initial_heartbeat_event_的延时配置在ReaderProxy所属的StatefulWriter中配置,具体是在WriterTimers结构中配置

c++ 复制代码
struct WriterTimes
{
    //! Initial heartbeat delay. Default value ~11ms.
    Duration_t initialHeartbeatDelay;    // 初始心跳包的发送延时,默认11毫秒
    //! Periodic HB period, default value 3s. 
    Duration_t heartbeatPeriod;          // 默认正常心跳包发送间隔,默认3秒
    //!Delay to apply to the response of a ACKNACK message, default value ~5ms.
    Duration_t nackResponseDelay;	     // 响应acknack的回复的延时,默认 5毫秒
    //!This time allows the RTPSWriter to ignore nack messages too soon after the data as sent, default value 0s.
    Duration_t nackSupressionDuration;   // 如果收到的ACKNACK中有missing的change,那么需要延时后才补发,这个就是对应的延时
}

Writer写入的CacheChange是否是通过ReaderProxy发给对端Reader的?如果不是,CacheChange发给Reader的流程是怎样的?

A: 不通过ReaderProxy发送给对端,可以在StatefulWriter中查找flow_controller_关键字,发送数据都是通过某一类FlowController(SyncFlowController/AsyncFlowController/PureSyncFlowController)来完成的

需要发送的Change需要添加给FlowController,FlowController通过FlowControllerFactory创建并且返回单例,提供了add_new_sample, add_old_sample,remove_change用于添加和移除要发送的CacheChange。

在ReaderProxy中是找不到对FlowController的直接或者间接调用 (提供过StatefulWriter的函数间接向FlowController中添加CacheChange)

写入ReaderProxy中CacheChange的状态变化是怎样的?

A:

RTPS中是如何完成Writer对后匹配的Reader做数据补发动作的?

A:

ReaderProxy在nack_supression_event_的定时器中做了什么事情?

A:

c++ 复制代码
bool ReaderProxy::perform_nack_supression()
{
    return 0 != convert_status_on_all_changes(UNDERWAY, UNACKNOWLEDGED);  // 将changes_for_reader_中UNDERWAY状态的Change改为UNACKNOWLEDGED,就是下次响应NACK的时候要带上这些Change一起发送
}

WriterProxy的作用?

A: 在Reliability为RELIABLE的情况下,当匹配到远端Writer的时候,StatefulReader为该远端Writer创建WriterProxy,用于保存该Writer的通信状态

(是否在先,发送的最后Change的SN,通信地址,GUID,GuidPrefix,是否发送过心跳包等等信息)

WriterProxy的主要成员如下:

c++ 复制代码
enum StateCode
{
  IDLE = 0, // WriterProxy没有执行关键操作(执行ACKNACK回复, 处理心跳包,执行stop等等)
  BUSY,     // WriterProxy正在执行关键操作
  STOPPED,  // WriterProxy已经停止,标识远端Writer无效
};  // 描述对端Writer状态
TimedEvent* heartbeat_response_;  // 用于响应HeartBeat子消息发送ACKNACK的定时器
TimedEvent* initial_acknack_;  // 初始化WriterProxy的时候用于发送第一包ACKNACK的定时器   (需要测试验证)
std::atomic<uint32_t> last_heartbeat_count_;   // 最后收到的HeartBeat子消息的序列号(Count)
std::atomic<bool> heartbeat_final_flag_;    // 标识收到的HeartBeat子消息的FinalFlag(设置了则不需要回,没设置则需要回复)
bool is_alive_;  // 标识对端Writer是否存活   (怎么判断的?)
pool_allocator_t changes_pool_;   // CacheChange池
foonathan::memory::set<SequenceNumber_t, pool_allocator_t> changes_received_;  // 保存收到的所有Data子消息的序列号
SequenceNumber_t changes_from_writer_low_mark_;  // 当前可以让Reader读取到内容的CacheChange的最高序列号(也就是收到了实际DATA子消息的),对应的CacheChange状态就是RECEIVED
              			   	     // 而changes_from_writer_low_mark+1对应的就是状态为missing的第一个CacheChange
SequenceNumber_t max_sequence_number_;  //  对端Writer可以获取的最大的CacheChange的序列号(可能有些还没有发Data,只发了HeartBeat)
SequenceNumber_t last_notified_;  //  最近一次通知DDS层DataReaderImpl读取的Changes中sequence的最大值
ResourceLimitedVector<GUID_t> guid_as_vector_;   // 保存对应的对端Writer的GUID
ResourceLimitedVector<GuidPrefix_t> guid_prefix_as_vector_;  // 保存对应的对端Writer的GUID Prefix
bool is_on_same_process_;  // 标识对端Writer是否和Reader在同一个进程中
uint32_t ownership_strength_;  // Qos中的Onwership Length属性,同一时刻当有多个Writer在Reader订阅的topic上发送数据时,ownership_strength_最高的那个Writer有高优先级被处理
   (The middleware will use the Ownership Strength to decide which DataWriter's data will be delivered to DataReaders. The DataWriter with the highest Ownership Strength value will be the owner of the instances and whose data is delivered to DataReaders. )  // https://community.rti.com/kb/summary-and-typical-use-cases-different-quality-service-qos-parameters
GUID_t persistence_guid_;  // PERSISTENCE服务的GUID(用于对Writer/Reader的History做持久化)
LocatorSelectorEntry locators_entry_;  // 对端Writer的Locator信息的入口
bool is_datasharing_writer_;  // 标识对端Writer是否时通过数据共享方式发送Data的
bool received_at_least_one_heartbeat_;  // 标识对端Writer是否发送过HeartBeat子消息
std::atomic<StateCode> state_;  // 对端Writer的状态

什么时候会发送DataFrag子消息(而不是发送Data子消息)?

用jni封装FastDDS的接口(白名单设置也最好加到接口中,读取数据接口包括主动和被动接收

相关推荐
IT毕设梦工厂7 分钟前
计算机毕业设计选题推荐-在线拍卖系统-Java/Python项目实战
java·spring boot·python·django·毕业设计·源码·课程设计
everyStudy32 分钟前
JS中判断字符串中是否包含指定字符
开发语言·前端·javascript
luthane34 分钟前
python 实现average mean平均数算法
开发语言·python·算法
Ylucius41 分钟前
动态语言? 静态语言? ------区别何在?java,js,c,c++,python分给是静态or动态语言?
java·c语言·javascript·c++·python·学习
凡人的AI工具箱1 小时前
AI教你学Python 第11天 : 局部变量与全局变量
开发语言·人工智能·后端·python
sleP4o1 小时前
Python操作MySQL
开发语言·python·mysql
hellojackjiang20111 小时前
即时通讯框架MobileIMSDK的H5端开发快速入门
网络·即时通讯·im开发
是店小二呀1 小时前
【C++】C++ STL探索:Priority Queue与仿函数的深入解析
开发语言·c++·后端
七夜zippoe1 小时前
分布式系统实战经验
java·分布式
洛寒瑜1 小时前
【读书笔记-《30天自制操作系统》-23】Day24
开发语言·汇编·笔记·操作系统·应用程序