RTPS协议之Messages Module子消息

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标识的样本对应的组序列号。

有效性

逻辑

触发条件

  1. Writer 的History溢出时主动发送
  2. 手动调用 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的目的有两个:

  1. 通知Reader Writer historyCache中的有效的序列号,以便Reader可以使用AckNack请求它可能错过的任何序列号
  2. 要求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。

核心功能:

  1. 通告数据的可用范围
  2. 触发丢包检测:Reader 对比本地接收的序列号与 HEARTBEAT 中的范围,发现缺失时回复 ACKNACK 请求重传。

触发条件

  1. 周期性发送(间隔由 HeartbeatPeriod QoS 控制)
  2. 新数据发送后立即发送(快速通知)

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 的值决定。

有效性

一直有效

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值