目录
Type定义
Type | Purpose |
---|---|
ProtocolId_t | |
SubmessageFlag | sub msg flag |
SubmessageKind | 枚举值用于标识是什么类型的sub msg,有这些:DATA, GAP, HEARTBEAT, ACKNACK, PAD, INFO_TS, INFO_REPLY, INFO_DST, INFO_SRC, DATA_FRAG, NACK_FRAG, HEARTBEAT_FRAG |
Time_t | 时间戳,至少到ns,TIME_ZERO,TIME_INVALID TIME_INFINITE |
Count_t | 保存递增的计数,用于识别重复消息 |
ParameterId_t | 用于在参数列表中唯一标识参数的类型。主要在Discovery中广泛使用,主要用于定义QoS参数。一些被保留用于协议定义的参数,另一些可以用于供应商定义的参数。 |
FragmentNumber_t | 用于保存帧序号? |
GroupDigest_t | 用于保存唯一标识属于同一参与者的一组实体的摘要值的类型。 |
RTPS消息结构
RTPS协议发送的每一条消息都有一个固定的长度。这个长度不是由RTPS协议明确发送的,而是作为传输底层的一部分,RTPS消息是通过这种方式发送的。在面向数据包的传输(例如UDP/IP)的情况下,消息的长度已经由传输头部提供。面向流的传输(例如TCP)则需要在消息前面插入长度,以便识别RTPS消息的边界
RTPS消息头
消息头标识这条消息属于RTPS协议,标识了协议版本和供应商,具体包含以下内容:
字段 | 类型 | 含义 | 抓包中对应的字段 |
---|---|---|---|
protocol | ProtocolId_t | 表示这个rtps消息 | Magic: RTPS |
version | ProtocolVersion_t | 标识rtps协议的版本号 | Protocol version: 2.2 |
vendorId | VendorId_t | RTPS协议实现的供应商标识 | vendorID: 01.15(eProsima - Fast-RTPS) |
guidPrefix | GuidPrefix_t | GUIDs中使用的默认的prefix?TODO要确认 |
- guidPrefix定义了一个默认前缀,可以用来重构消息中包含的子消息的全局唯一标识符(GUIDs)。guidPrefix允许子消息只包含GUID的EntityId部分,因此可以避免在每个GUID上重复相同的前缀,从而节省空间。
子消息结构
每个RTPS消息都是由一个或多个子消息组成。
所有的子消息都是由 一个SubmessageHeader+0个或多个消息元素 组成。子消息头部用于标识子消息的种类和该子消息内的可选元素。
子消息头
子消息头结构:
字段 | 类型 | 含义 |
---|---|---|
submessageId | SubmessageKind | 标识了Submessage.的消息类型 |
flags | SubmessageFlag[8] | 标识用于编码子消息的字节顺序,子消息中的可选元素的存在,并可能修改子消息的解释。有8个可能的标志。第一个标志(索引0)标识用于编码子消息的字节顺序。其余的标志会根据子消息的种类有不同的解释,并分别为每个子消息进行描述。 |
submessageLength | ushort | 表示子消息的长度。由于RTPS消息由子消息的连结组成,所以子消息的长度可用于跳转到下一个子消息。 |
SubmessageId
:子消息ID用于识别子消息的类型。有效的ID由SubmessageKind的可能值枚举(RTPS协议文档formal-9-04-03:157):
enum SubmessageKind {
PAD = 0x01, /* Pad */
ACKNACK = 0x06, /* AckNack */
HEARTBEAT = 0x07, /* Heartbeat */
GAP = 0x08, /* Gap */
INFO_TS = 0x09, /* InfoTimestamp */
INFO_SRC = 0x0c, /* InfoSource */
INFO_REPLY_IP4 = 0x0d, /* InfoReplyIp4 */
INFO_DST = 0x0e, /* InfoDestination */
INFO_REPLY = 0x0f, /* InfoReply */
NACK_FRAG = 0x12, /* NackFrag */
HEARTBEAT_FRAG = 0x13, /* HeartbeatFrag */
DATA = 0x15, /* Data */
DATA_FRAG = 0x16, /* DataFrag */
};
FastDDS中定义如下:
// //!@brief Enumeration of the different Submessages types
enum SubmessageId : uint8_t
{
PAD = 0x01,
ACKNACK = 0x06,
HEARTBEAT = 0x07,
GAP = 0x08,
INFO_TS = 0x09,
INFO_SRC = 0x0c,
INFO_REPLY_IP4 = 0x0d,
INFO_DST = 0x0e,
INFO_REPLY = 0x0f,
NACK_FRAG = 0x12,
HEARTBEAT_FRAG = 0x13,
DATA = 0x15,
DATA_FRAG = 0x16
};
flags
:子消息头中的标志包含8个布尔值。第一个标志,即EndiannessFlag,存在于所有子消息中并位于相同位置,代表用于编码子消息信息的字节序。
如果EndiannessFlag设置为FALSE
,子消息将以大端格式编码,EndiannessFlag设置为TRUE
则表示以小端格式编码。其他标志的解释取决于子消息的类型。submessageLength
:表示Submessage的长度,如果submessageLength > 0
,有以下两种情况:- 从子消息内容的开始到下一个子消息头的开始的长度(如果该子消息不是消息中的最后一个子消息)。
- 或者它是剩余的消息长度(如果子消息是消息中的最后一个子消息)。解读消息的解析器可以区分这两种情况,因为它知道消息的总长度。
如果子消息长度等于0,那么子消息就是消息中的最后一个子消息,并且会延伸到消息的结束。这使得发送大于64k的子消息成为可能(这是可以存储在submessageLength字段中的最大长度),只要它们是消息中的最后一个子消息。
具体子消息的类型和解释,请参考这里
RTPS消息接收者
一个消息中的子消息的解释和含义可能取决于该消息中之前的子消息。因此,消息的接收者必须维护同一消息中先前反序列化的子消息的状态。这种状态被建模为每次处理新消息时复位的RTPS接收器的状态,并为每个子消息的解释提供上下文。
对每一条新的message,Receiver的状态按下表列出的内容被重置和初始化。对应Fast DDS中的MessageReceiver
类
name | 初始值 |
---|---|
sourceVersion | PROTOCOLVERSION |
sourceVendorId | VENDORID_UNKNOWN |
sourceGuidPrefix | GUIDPREFIX_UNKNOWN |
destGuidPrefix | 接收消息的participant的guid prefix |
UnicastReplyLocatorList | |
multicastReplyLocatorList | |
haveTimestamp | False |
timestamp | TIME_INVALID |
messageLength |
Message Receiver必须遵守以下规则:
- 如果Submessage的消息头不能被读取,则其他消息体无效
- submessageLength为下一个Submessage的起始位置,或指示子消息扩展到消息的结束。如果这个字段是无效的,那么这条消息无效。
- 具有未知SubmessageId的子消息必须被忽略,并且必须要解析继续下一个子消息。也就是说,不在SubmessageKind范围内的消息id必须要被忽略。未知vendorId的供应商的SubmessageIds也必须被忽略,并且必须解析继续到下一个子消息。
- Submessage的接收者应忽略未知的flag。RTPS2.4的实现跳过了所有被标记为“X”的flags
- 一个有效的submessageLength字段必须要被用于发小下一条Submessage, 即使Submessage有明确的id
- 一个已知但无效的子消息会使消息的其余部分失效。
子消息何时被视为无效,接收到有效的头部和/或子消息,对于接收者有两个影响::
- 它可以改变接收者的状态;这个状态影响了消息中后续子消息的解释方式。RTPS Submessages部分会讨论每个子消息如何改变状态。在此协议版本中,只有Header和子消息
InfoSource
,InfoReply
,InfoDestination
和InfoTimestamp
改变了接收者的状态。 - 它可以影响消息的目标Endpoint的行为,这适用于基本的RTPS消息:
Data
,DataFrag
,HeartBeat
,AckNack
,Gap
,HeartbeatFrag
,NackFrag
.
RTPS SubmessageElements
每个RTPS消息包含可变数量的RTPS子消息。每个RTPS子消息反过来又是由一组预定义的原子构建块构成,这些构建块被称为SubmessageElements(子消息元素)。RTPS 2.4定义了以下子消息元素:GuidPrefix
, EntityId
, SequenceNumber
, SequenceNumberSet
, FragmentNumber
, FragmentNumberSet
, VendorId
, ProtocolVersion
, LocatorList
, Timestamp
, Count
, SerializedData
, ParameterList
和GroupDigest
。
- GuidPrefix和EntityId:GUID_t的组成部分
- VendorId:供应商id
- ProtocolVersion:协议版本
- SequenceNumber:64位带符号的整数,值的范围为:-2^63 <= N <= 2^63-1,
- SequenceNumberSet:SequenceNumberSet被限制在小于256的一个区间内,换句话说,一个有效的SequenceNumberSet必须要校验:
maximum(SequenceNumberSet) - minimum(SequenceNumberSet) < 256
minimum(SequenceNumberSet) >= 1
SequenceNumberSet子元素可以用于选择性地请求重新发送一组序列号。SequenceNumberSet子元素的结构:
字段 | 类型 | 含义 |
---|---|---|
base | SequenceNumber_t | 集合中的第一个数据 |
set | SequenceNumber_t[*] | 一个连续的集合,每个都会校验:base <= element(set) <= base+255 |
- FragmentNumber:32位无符号整数,用于Submessage标识一个分片序列化的特定的分片。
- FragmentNumberSet
- Timestamp:时间
- ParameterList
- Count
- LocatorList:locators的列表
字段 | 类型 | 含义 |
---|---|---|
value | Locator_t[*] | locators的列表 |
- SerializedData:SerializedData包含数据对象值的序列化表示。RTPS协议不会解释序列化的数据流,因此这个数据流是无法直接能看懂的。
字段 | 类型 | 含义 |
---|---|---|
value | octet[*] | 序列化的数据流 |
- SerializedDataFragment:分片的数据流。和上面一样,rtps协议不会解释数据流,所以这个数据流是晦涩难懂的
字段 | 类型 | 含义 |
---|---|---|
value | octet[*] | 序列化的数据流 |
- GroupDigest:GroupDigest用于以紧凑的方式传递一组EntityId_t。
字段 | 类型 | 含义 |
---|---|---|
value | GroupDigest_t | 序列化的数据流 |
RTPS Header
每一条 RTPS Message必须以一个 Header开头.
目的
头部用于将消息标识为属于RTPS协议,标识使用的RTPS协议的版本,并提供适用于消息中包含的子消息的上下文信息。
内容
有效性
当出现以下任何情况时,头部是无效的:
- 消息的字节数小于包含完整头部所需的字节数。所需的数量由PSM定义。
- 其协议值不匹配PROTOCOL_RTPS2的值。
- 主要协议版本大于实现支持的主要协议版本。
RTPS Submessages
RTPS协议定义了不同类型的Submessages。划分为两类:Entity-Submessages和Interpreer-Submessages。Entity-Submessages的目标是RTPS Entity;Interpreer-Submessages修改RTPS Receiver的状态,并提供有助于处理后续实体子消息的上下文。
Entity Submessages
- Data:包含application的Data对象的信息,Data Submessages由Writers发送给Readers。
- DataFrag:等同于Data,但仅包含新值的一部分(一个或多个片段)。允许将数据作为多个片段进行传输,以克服传输消息大小的限制。
- Heartbeat:描述在Writer中可用的信息。Writer会向一个或多个Reader发送Heartbeat消息。
- HeartbeatFrag:对于分片数据,描述了在Writer中可用的分片。Writer会向一个或多个reader发送HeartbeatFrag消息。
- Gap:描述对Reader来说已经不再相关的信息。Writer会向一个或多个Reader发送Gap消息。
- AckNack: 向Writer提供Reader的状态信息。AckNack消息由Reader发送给一个或多个Writer。
- NackFrag: 向Writer提供Reader的状态信息,更具体地说,Reader还缺少哪些片段。NackFrag消息由Reader发送给一个或多个Writer。
Interpreter Submessages
- InfoSource: 提供关于后续子Entity Submessages来源的信息。这个子消息主要用于中继 RTPS 子消息。这在当前的规范中并未讨论。
- InfoDestination: 提供关于后续子Entity Submessages最终目的地的信息。这个子消息主要用于中继RTPS子消息。这在当前的规范中并未讨论。
- InfoReply: 为后续子消息中出现的实体提供回复位置的信息。
- InfoTimestamp: 为后续的Entity Submessages提供源端的时间戳
- Pad: 如果需要进行内存对齐,可以用来给消息添加填充。
RTPS Submessage类图