目录
AckNack
目标
这个子消息用于将Reader的状态传达给Writer。这个子消息允许Reader通知Writer它接收到的序列号和它仍然缺少的序列号。既可以用于正向确认,也可以用于负向确认(正面确认代表Reader成功接收到了某一序列号的数据,负面确认则表示Reader没有接收到某一序列号的数据,需要Writer重新发送。简单来说,正面确认就是“我收到了你的数据”,而负面确认就是“我没收到你的数据,需要你重新发送”。)。
内容
AckNack消息结构如下:
- EndiannessFlag
- 类型:SubmessageFlag
- 含义:出现在Submessage的header flags,表明字节序
- FinalFlag
- 类型:SubmessageFlag
- 含义:出现在Submessage的header flags中,指示Writer是否必须响应。
- readerId
- 类型:EntityId
- 含义:用于识别确认接收某些序列号和/或请求接收某些序列号的Reader entity。
- writerId
- 类型:EntityId
- 含义:这个writerId指定的Writer是作为AckNack消息目标的Writer。这个Writer是被要求重新发送某些序列号,或被通知接收某些序列号的Writer。
- readerSNState
- 类型:SequenceNumberSet
- 含义:将读取器的状态传达给写入器。所有直到readerSNState.base前一个的序列号都被确认为被reader已接收。出现在集合中的序列号表示读取器端缺少的序列号。未出现在集合中的序列号是不确定的(可能已接收,也可能未接收)。
- count
- 类型:Count
- 含义:每次发送新的AckNack消息时,计数器都会增加。它为Writer提供了一种检测重复AckNack消息的手段,这些重复的消息可能由于冗余通信路径的存在而产生。
有效性
当出现以下情况时,AckNack子消息是无效的:
- Submessage header中的submessageLength太小
- readerSNState是无效的
逻辑
Reader通过发送AckNack消息给Writer,来表示其对Writer使用的序列号的状态。
Writer由其GUID(全局唯一标识符)唯一标识。Writer的GUID可以通过接收器的状态获取:
writerGUID = { Receiver.destGuidPrefix, AckNack.writerId }
Reader由其GUID唯一标识。Reader的GUID可以通过接收器的状态获取:
readerGUID = { Receiver.sourceGuidPrefix, AckNack.readerId }
这个消息同时满足两个目的:
- 这个子消息确认所有的序列号,直到并包含在序列号集合中最低序列号之前的那个(即readerSNState.base -1)。
- 这个子消息否定确认(请求)在集合中明确出现的序列号。
序列号的明确机制取决于PSM。通常情况下,使用一种紧凑的表示(比如位图)。
FinalFlag标记表明Reader是否期望Writer的心跳,或者决定权是否交给写入器。
Data
这个子消息是由RTPS Writer发送给RTPS Reader
目标
子消息通知RTPS读取器,RTPS写入器属于的数据对象发生了变化。可能的变化包括数据对象的值的变化以及数据对象生命周期的变化。
内容
Data Submessage结构如下所示
- EndiannessFlag
- 类型:SubmessageFlag
- 含义:出现在Submessage header flags中,表示字节序
- InlineQosFlag
- 类型:SubmessageFlag
- 含义:出现在Submessage header flags中,表示出现在Reader的参数列表中包含Qos参数用于解释消息
- DataFlag
- 类型:SubmessageFlag
- 含义:出现在Submessage header flags中,告诉reader,dataPayload子消息元素中包含data-object的序列化后的值
- KeyFlag
- 类型:SubmessageFlag
- 含义:出现在Submessage header flags中,告诉reader,dataPayload子消息元素包含了数据对象key的序列化值。
- NonStandardPayloadFlag
- 类型:SubmessageFlag
- 含义:出现在Submessage header flags,告诉Reader,serializedPayload子消息元素并未按照第10节 TODO 的格式进行格式化。
- readerId
- 类型:EntityId
- 含义:表示接收data-object的RTPS Reader的entityid
- writerId
- 类型:EntityId
- 含义:发送或者说是新增这个change的data-object的RTPS Writer
- writerSN
- 类型:SequenceNumber
- 含义:唯一标识由writerGuid标识的RTPS Writer所做的所有更改及其相对顺序。每个更改都获得一个连续的序列号。每个RTPS Writer都维护自己的序列号。
- inlineQos
- 类型:ParameterList
- 含义:只有在header中设置了InlineQosFlag时才存在,包含可能影响消息解读的QoS。
- serializedPayload
- 类型:SerializedPayload
- 含义:只有当DataFlag 或者 KeyFlag在header里被设置才出现。如果设置了DataFlag,它包含data-object的新值;如果KeyFlag被设置,它包含消息指向的data-object的key
有效性
当满足以下任一条件时,此子消息无效:
- 子消息头部的submessageLength太小。
- writerSN.value不是严格正数(1,2,…)或者是SEQUENCENUMBER_UNKNOWN。
- inlineQos无效。
逻辑
Writer由GUID唯一标识. Writer GUID通过Receiver的状态获取: writerGUID = { Receiver.sourceGuidPrefix, Data.writerId }
Reader由它的GUID唯一标识. Reader GUID 由Receiver的状态获取: readerGUID = { Receiver.destGuidPrefix, Data.readerId }
Data的readerId可以是ENTITYID_UNKNOWN
,在这种情况下,Data适用于由GuidPrefix_t
Receiver.destGuidPrefix
标识的participant中的所有writerGUID
的读者。
DataFrag
由RTPS Writer发送给RTPS Reader
目标
DataFrag子消息扩展了Data子消息,使得serializedData可以被分片为多个DataFrag子消息发送;RTPS reader会重新组装DataFrag子消息中包含的分片。
定义一个分离的DataFrag Submessage除了Data子消息之外,还提供了如下优点:
- 它使每个子消息的内容和结构变化保持在最小,这使得协议的实现更为高效,因为网络包的解析得以简化
- 避免了在Data子消息中添加分片信息作为inline qos参数,这不仅会降低性能,而且还使得在线调试变得更加困难,因为数据是否分片以及哪个消息包含哪个分片已经不再明显。
内容
DataFrag Submessage的结构如下:
- EndiannessFlag
- 类型:SubmessageFlag
- 含义:出现在Submessage header flags中,表示字节序
- InlineQosFlag
- 类型:SubmessageFlag
- 含义:出现在Submessage header flags中,表示存在一个包含应用于解释消息的qos参数的参数列表。
- NonStandardPayloadFlag
- 类型:SubmessageFlag
- 含义:出现在Submessage header flags中,表示serializedPayload子消息元素并未按照第10节的格式进行格式化。
- KeyFlag
- 类型:SubmessageFlag
- 含义:出现在Submessage header flags中,表示dataPayload子消息元素包含data对象key的序列化值。
- readerId
- 类型:EntityId
- 含义:要接受data-object的change的RTPS Reader
- writerId
- 类型:EntityId
- 含义:要发送data- object的change的RTPS Writer
- writerSN
- 类型:SequenceNumber
- 含义:在RTPS Writer端(由writerGuid标识)所有的change中唯一标识这个change,和一个相对的顺序,每一次的change都会获得一个连续的序列号,每一个 RTPS Writer都需要维护自己的序列号。
- fragmentStartingNum
- 类型:FragmentNumber
- 含义:serializedData中的一系列分片的起始片段。分片编号从1开始。
- fragmentsInSubmessage
- 类型:ushort
- 含义:这个子消息中包含的连续片段的数量,从fragmentStartingNum开始。
- dataSize
- 类型:ulong
- 含义:分片前原始数据的大小
- fragmentSize
- 类型:ushort
- 含义:一个单独片段的大小,以字节为单位。最大片段大小等于64K。
- inlineQos
- 类型:ParameterList
- 含义:只有InlineQosFlag出现在header中时才有意义。包含的Qos可能会影响对message的解释
- serializedPayload
-
类型:SerializedPayload
-
含义:一个连续的分片序列,从fragmentStartingNum开始,总共有fragmentsInSubmessage个分片。代表了data-object改变后的新值的一部分。
- 如果KeyFlag没有设置,那么它包含了data-object 改变后新值的连续片段集合。
- 如果KeyFlag设置了,那么它包含了消息引用的data-object的key的连续片段集合。
在任何情况下,连续的片段集合都包含fragmentsInSubmessage个片段,并从由fragmentStartingNum标识的片段开始。
-
有效性
逻辑
DataFrag子消息的解释与Data子消息的解释相同。以下是使用DataFrag子消息中的信息重新组装serializedData的方法:
数据总大小由 dataSize 给出。每个DataFrag子消息在其serializedData元素中包含这些数据的连续段。段的大小由serializedData元素的大小决定。在重新组装过程中,每个段的偏移量由以下公式确定:
(fragmentStartingNum - 1) * fragmentSize
所以,你需要按照这个偏移量来对序列化的数据重新进行排序,以确保数据的完整性和正确性。一旦所有的片段都被正确地收集和排序,就可以通过反序列化过程将数据恢复到其原始格式。
当所有片段都已接收时,数据将完全重新组装。预期的片段总数等于:
(dataSize / fragmentSize) + ((dataSize % fragmentSize) ? 1 : 0)
注意,每个DataFrag子消息可能包含多个片段。 RTPS Writer将根据所有底层传输中支持的最小消息大小选择fragmentSize。如果一些RTPS Reader可以通过支持更大消息的传输来达到(?TODO),那么RTPS Writer可以将多个片段打包到一个DataFrag子消息中,或者如果不再需要片段化,甚至可以发送常规的Data子消息。
在使用DataFrag子消息发送inlineQos时,只需要在给定的Writer序列号的第一个DataFrag子消息中发送inlineQos即可。对于给定的Writer序列号,每个DataFrag子消息中都发送相同的inlineQos是多余的。
Gap
目标
RTPS Writer发送给RTPS Reader,向RTPS Reader表明一些列的sequence number不再有效。这个集合可能是一个连续的序列号范围,也可能是一个特定的序列号集合。
内容
Gap消息的结构如下:
- EndiannessFlag
- 类型:SubmessageFlag
- 含义:出现在Submessage header flags中,表示字节序
- GroupInfoFlag
- 类型:SubmessageFlag
- 含义:表示发送者所属的writers(Writer Group)有额外的信息。
- readerId
- 类型:EntityId
- 含义:标识正在被通知一组序列号无关的reader entity
- writerId
- 类型:EntityId
- 含义:标识应用一系列序列号的writer entity。
- gapStart
- 类型:SequenceNumber
- 含义:标识在无关序列号区间中的第一个序列号。
- gapList
- 类型:SequenceNumberSet
- 含义:有两个目的:(1) 标识在无关序列号区间中的最后一个序列号。(2) 标识一组额外的无关序列号。
- gapStartGSN
- 类型:SequenceNumber
- 含义:仅当在头部设置了GroupInfoFlag时才出现。标识对应于gapStart标识的样本的组序列号。
- gapEndGSN
- 类型:SequenceNumber
- 含义:仅当在头部设置了GroupInfoFlag时才出现。标识从gapStartGSN开始的一系列连续GSN的结束,这些GSN对reader不可用。它应大于或等于与gapList.bitmapBase标识的样本对应的组序列号。
有效性
逻辑
触发条件
- Writer 的History溢出时主动发送
- 手动调用 API(如 RTPSWriter::send_gap())强制发送。
Heartbeat
目标
RTPS Writer发送给RTPS Reader,传递在Writer端有效的changes的序列号
内容
Heartbeat message结构如下:
- EndiannessFlag
- 类型:SubmessageFlag
- 含义:出现在Submessage header flags中,表示字节序
- FinalFlag
- 类型:SubmessageFlag
- 含义:出现在Submessage header flags中,表明Reader是否需要回复Heartbeat或者仅仅是一个咨询心跳。
- LivelinessFlag
- 类型:SubmessageFlag
- 含义:出现在Submessage header flags中,表明与RTPS Writer关联的DDS DataWriter已手动断言其活跃性(?)。
- GroupInfoFlag
- 类型:SubmessageFlag
- 含义:出现在Submessage header flags中,表明发送者所属的writer群组(Writer Group)的附加信息的存在。
- readerId
- 类型:EntityId
- 含义:这用于标识那些被告知一组序列号可用的Reader Entity。它可以被设置为ENTITYID_UNKNOWN,以指示发送消息的writer的所有reader(都可以接收?)。
- writerId
- 类型:EntityId
- 含义:用于标识那个序列号范围适用的Writer Entity。
- firstSN
- 类型:SequenceNumber
- 含义:如果Writer中有可用的samples,则表明第一个(最低的)可用的sequence number;如果Writer中没有可用的samples,则表明还没有被writer写入的最低的sequence number
- lastSN
- 类型:SequenceNumber
- 含义:表示Writer写入的最后一个sequence number
- count
- 类型:Count
- 含义:是一个计数,每次发送Heartbeat消息时都会递增。为Reader提供了检测可能由于冗余通信路径导致的重复Heartbeat的方法
- currentGSN
- 类型:SequenceNumber
- 含义:只有当Header中的GroupInfoFlag出现时才有效。表示在发送HeartBeat时,Wrtier group中任何DataWriter写入的最后(最高)的group sequence number
- firstGSN
- 类型:SequenceNumber
- 含义:只有在Header中的GroupInfoFlag出现时才有效,表示与sequence number firstGSN对应的group sequence number
- lastGSN
- 类型:SequenceNumber
- 含义:只有在Header中的GroupInfoFlag出现时才有效。表示与sequence number lastGSN对应的group sequence number
- writerSet
- 类型:GroupDigest
- 含义:只有在Header中的GroupInfoFlag出现时才有效。标识在写入具有currentGSN的样本时,属于Writer’s Group的Writer的子集。
- secureWriterSet
- 类型:GroupDigest
- 含义:只有在Header中的GroupInfoFlag出现时才有效,留给DDS-安全规范使用。
有效性
逻辑
HeartBeat的目的有两个:
- 通知Reader Writer historyCache中的有效的序列号,以便Reader可以使用AckNack请求它可能错过的任何序列号
- 要求Reader发送一个确认,说明CachaChange的changes已经进入到reader的HistoryCache中,以便Writer了解Reader的状态
所有的Heartbeat messages都具备第一个目的,也就是说,Reader将始终了解Writer的HistoryCache状态,并可能请求它所遗漏的内容。通常情况下,RTPS Reader只有在缺少CacheChange的情况时才会发送AckNack消息
Writer使用FinalFlag来要求Reader发送对已收到的序列号的确认。如果一个Heartbeat消息的FinalFlag被设置了,那么Reader无需发送一个AckNack消息回复,如果FinalFlag没有被设置,即使是AckNack表示他已经收到了所有在Writer的HistoryCache中的CacheChange changes,Reader也必须发送一个AckNack消息表明它收到了哪些CacheChange changes。
Writer设置了LivelinessFlag表明,与RTPS Writer关联的DDS DataWriter使用DDS标准中指定的方式手动确认其liveliness,因此RTPS Reader应该更新对应的远端DataWriter的liveliness lease。
Writer设置GroupInfoFlag表明 currentGSN, firstGSN, lastGSN, writerSet, and secureWriterSet元素的存在。这些字段提供了Writer Group中 与Writer关联的CacheChanges。
Writer通过其GUID进行唯一识别,Writer的GUID是使用Receiver的状态获得的:
writerGUID = { Receiver.sourceGuidPrefix, Heartbeat.writerId }
Reader通过其GUID进行唯一识别,Reader的GUID是使用Receiver的状态获得的:
readerGUID = { Receiver.destGuidPrefix, Heartbeat.readerId }
Heartbeat.readerId可以是ENTITYID_UNKNOWN,在这种情况下,Heartbeat适用于participant内部的这个writerGUID的所有Readers。
核心功能:
- 通告数据的可用范围
- 触发丢包检测:Reader 对比本地接收的序列号与 HEARTBEAT 中的范围,发现缺失时回复 ACKNACK 请求重传。
触发条件
- 周期性发送(间隔由 HeartbeatPeriod QoS 控制)
- 新数据发送后立即发送(快速通知)
HeartbeatFrag
目标
在对数据进行分片并且直到所有分片都可用时,HeartbeatFrag子消息从RTPS Writer发送到RTPS Reader,以通知Reader 端在Writer端有哪些分片可用。这使得在分片级别可以进行可靠的通信。
一旦所有片段都可用,就会使用常规的心跳消息。
内容
- EndiannessFlag
- 类型:SubmessageFlag
- 含义:出现在Submessage header flags中,表示字节序
- readerId
- 类型:EntityId
- 含义:标识正在被通知分片可用的Reader实体。可以设置为ENTITYID_UNKNOWN,以指示发送消息的Writer的所有Reader(都被通知到了?)。
- writerId
- 类型:EntityId
- 含义:表示发送Submessage的Writer Entity
- writerSN
- 类型:SequenceNumber
- 含义:表示可用分片的data change的序列号。
- lastFragmentNum
- 类型:FragmentNumber
- 含义:表示在Writer端,对所有的被writerSN标识的change,直到这个最后最高分片为止的所有分片在writer上都是可用的
- count
- 类型:Count
- 含义:这是一个计数器,每发送一次新的HeartbeatFrag消息就会增加。这为Reader提供了检测重复的HeartbeatFrag消息的方法,这些消息可能由于冗余的通信路径而产生。
有效性
以下情况下无效:
- Submessage header中的submessageLength太小
- writerSN.value为0或者负数
- lastFragmentNum.value为0或为负数
逻辑
HeartbeatFrag消息的作用与常规Heartbeat消息一样,但它指的不是一系列序列号的可用性,而是具有序列号WriterSN的data change的一系列片段的可用性。
RTPS Reader将通过发送NackFrag消息进行回应,但前提是它缺少任何可用的片段。Writer由其GUID唯一标识。Writer GUID可以通过接收方的状态获得:
writerGUID = { Receiver.sourceGuidPrefix, HeartbeatFrag.writerId }
Reader由其GUID唯一标识。Reader GUID可以通过接收方的状态获得:
readerGUID = { Receiver.destGuidPrefix, HeartbeatFrag.readerId }
HeartbeatFrag.readerId
可以是ENTITYID_UNKNOWN
,在这种情况下,HeartbeatFrag适用于participant内该Writer GUID的所有Reader。
InfoDestination
目标
RTPS Writer发送给RTPS Reader,用于修改用来解释接下来的子消息中出现的reader entityID的GuidPrefix。
内容
- EndiannessFlag
- 类型:SubmessageFlag
- 含义:出现在Submessage header flags中,表示字节序
- guidPrefix
- 类型:GuidPrefix
- 含义:提供应该用于重构所有RTPS reader entity的GUID的GuidPrefix,这些entities的EntityIds出现在后续的子消息中。
有效性
Submessage header中的submessageLength太小时无效
Receiver状态改变
if (InfoDestination.guidPrefix != GUIDPREFIX_UNKNOWN) {
Receiver.destGuidPrefix = InfoDestination.guidPrefix
} else {
Receiver.destGuidPrefix = <GuidPrefix_t of the Participant receiving
the_message>
}
InfoReply
目标
RTPS Reader发送给RTPS Writer,它包含了关于在哪里发送对其后跟的子消息的显式信息(?)
内容
- EndiannessFlag
- 类型:SubmessageFlag
- 含义:出现在Submessage header flags中,表示字节序
- MulticastFlag
- 类型:SubmessageFlag
- 含义:出现在Submessage header flags中,表示Submessage是否包含了multicast地址
- unicastLocatorList
- 类型:LocatorList
- 含义:表示Writer在回复跟随的子消息时应使用的一组替代单播地址以到达Reader。
- multicastLocatorList
- 类型:LocatorList
- 含义:表示Writer在回复跟随的子消息时应使用的一组替代多播地址以到达Reader。仅在设置了MulticastFlag时出现。
有效性
Submessage header中的submessageLength太小时无效
Receiver状态改变
Receiver.unicastReplyLocatorList = InfoReply.unicastLocatorList
if ( MulticastFlag ) {
Receiver.multicastReplyLocatorList = InfoReply.multicastLocatorList
} else {
Receiver.multicastReplyLocatorList = <empty>
}
InfoSource
目标
此消息修改了后续子消息的逻辑源。
内容
- EndiannessFlag
- 类型:SubmessageFlag
- 含义:出现在Submessage header flags中,表示字节序
- protocolVersion
- 类型:ProtocolVersion
- 含义:表明后续子消息使用的协议。
- vendorId
- 类型:VendorId
- 含义:表示发起后续子消息的供应商的VendorId。
- guidPrefix
- 类型:GuidPrefix
- 含义:标识包含RTPS Writer entity的participant,该实体是随后的子消息的来源。
有效性
当Submessage header中的submessageLength太小时无效
Receiver状态的修改
Receiver.sourceGuidPrefix = InfoSource.guidPrefix
Receiver.sourceVersion = InfoSource.protocolVersion
Receiver.sourceVendorId = InfoSource.vendorId Receiver.unicastReplyLocatorList = { LOCATOR_INVALID } Receiver.multicastReplyLocatorList = { LOCATOR_INVALID }
haveTimestamp = false
问题
什么场景下会用到InfoSource子消息?
InfoTimestamp
目标
子消息用于发送一个时间戳,它适用于在同一消息中的后续子消息。(TODO 什么是同一消息的后续子消息?)
内容
- EndiannessFlag
- 类型:SubmessageFlag
- 含义:出现在Submessage header flags中,表示字节序
- InvalidateFlag
- 类型:SubmessageFlag
- 含义:表示是否应将后续的子消息视为具有时间戳
- timestamp
- 类型:Timestamp
- 含义:仅在header中未设置InvalidateFlag时存在。 包含应用于解释后续子消息的时间戳。
有效性
当在Submessage header中的submessageLength太小时无效
Receiver的状态改变
if ( !InfoTimestamp.InvalidateFlag ) {
Receiver.haveTimestamp = true
Receiver.timestamp = InfoTimestamp.timestamp
} else {
Receiver.haveTimestamp = false
}
NackFrag
目标
这个子消息的目的是用于将reader的状态传递给writer。当一个data change作为一系列分片发送时,NackFrag子消息允许reader通知Writer仍然缺少特定的分片编号
这个子消息只能包含负面确认,这与AckNack Submessage不同,AckNack包含正面和负面确认。这种方式的优点包括:
- 它消除了AckNack Submessage引入的窗口限制。考虑到SequenceNumberSet 的大小被限制为256,AckNack Submessage只能对序列号不超过第一个缺失样本256个以上的样本进行负面确认,第一个缺失的sample以下的任何的samples都会得到确认。另一方面,NackFrag可以用于任何分片编号进行NACK,即使是在早期AckNack子消息中进行NACK的碎片相距超过256的碎片。当处理包含大量碎片的样本时,这一点变得非常重要。(?)
- 可以按任何顺序对分片进行负面确认
内容
- EndiannessFlag
- 类型:SubmessageFlag
- 含义:出现在Submessage header flags中,表示字节序
- readerId
- 类型:EntityId
- 含义:要求接收特定分片的Reader entity
- writerId
- 类型:EntityId
- 含义:NackFrag消息的目标Writer entity,这是被要求重新发送一些分片的writer entity
- writerSN
- 类型:SequenceNumber
- 含义:缺失分片的序列号
- fragmentNumberState
- 类型:FragmentNumberSet
- 含义:reader 将状态通知给Writer。出现在set中的分片号表示Reader端缺失的分片,没有出现在set中的则是未确定的(有可能收到,也有可能没有收到)
- count
- 类型:Count
- 含义:每次发送新的 NackFrag 消息时,该计数器会增加。它为Writer提供了检测重复 NackFrag 消息的手段,这些重复的消息可能由于冗余通信路径的存在而产生。
有效性
当出现以下情况时这个子消息无效:
- 子消息头部的子消息长度过小
- writerSN.value为零或负数
- fragmentNumberState无效
逻辑
Reader向Writer发送NackFrag消息,用来请求Writer端的分片(fragments )。Writer由其GUID唯一标识,Writer GUID是通过接收器的状态获取:writerGUID = { Receiver.destGuidPrefix, NackFrag.writerId }
;Reader由其GUID唯一标识,Reader的GUID是通过Receiver接收器的状态获取:readerGUID = { Receiver.sourceGuidPrefix, NackFrag.readerId },请求的分片的序列号由writerSN给出,明确表示分片号的机制取决于PSM,通常使用更紧凑的表示方式如位图。
Pad
目标
这个子消息的主要作用是为了满足内存对齐需求而填充的信息,没有任何含义
内容
该子消息没有内容。它仅通过子消息的头部来完成其目的。pading数量由 submessageLength 的值决定。
有效性
一直有效