欢迎光临
我们一直在努力

车载以太网之要火系列 - 第51篇郭大侠学DDS(RTPS报文):底层报文长啥样 一看报头不一样

写在开篇·蓉儿继续挖坑

上回说到,郭靖搞清楚了DDS的核心思想——发布-订阅,以数据为中心。发布者只管发,订阅者只管收,中间由DDS管理。

郭靖合上笔记本,信心满满:“蓉儿,DDS的Topic我懂了!就是数据的‘名字’,用字符串标识,一看就懂。”

黄蓉咬了口糖葫芦:“那好,我问你——DDS的报文长啥样?它跟SOME/IP报文有什么区别?你怎么一眼认出它是DDS报文?”

郭靖一愣:“这……”

“DDS底层跑的是RTPS协议。 今天就把RTPS报文格式讲透。读完这篇,你抓包看到RTP S这几个字节,就知道‘哦,这是DDS在说话’。”

附:RTPS协议速查表

💡 RTPS的背景:DDS是一个API标准,定义了“怎么用”。但不同的DDS厂家实现不同,互相之间不能通信。于是OMG组织定义了RTPS(Real-Time Publish-Subscribe Protocol),作为DDS的“有线协议”,让不同厂家的DDS产品能够互操作。

项目说明
全称 Real-Time Publish-Subscribe Protocol
作用 DDS的底层有线协议,定义报文格式和交互行为
传输层 UDP/IP(通常),也支持TCP
端口 动态分配,通过发现机制协商
与SOME/IP的区别 SOME/IP头部12字节有Service ID/Method ID;RTPS更复杂,支持发现、可靠传输、分片等

一、RTPS是什么?为什么DDS需要它?

郭靖问:“SOME/IP直接跑在UDP上,报文头就12个字节。DDS为什么要搞个RTPS?”

黄蓉画了一张对比图:

text

┌─────────────────────────────────────────────────────────────────────┐
│ SOME/IP vs DDS/RTPS │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ SOME/IP: │
│ ┌─────────────┐ │
│ │ SOME/IP报文 │ ──→ UDP ──→ IP ──→ 以太网 │
│ │ (12字节头) │ │
│ └─────────────┘ │
│ │
│ DDS/RTPS: │
│ ┌─────────────────────────────────────────────┐ │
│ │ RTPS消息(Header + 多个Submessage) │ │
│ │ ├── 发现子消息(PDP/EDP) │ │
│ │ ├── 心跳子消息(Heartbeat) │ │
│ │ ├── 确认子消息(AckNack) │ │
│ │ └── 数据子消息(Data) │ │
│ └─────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ UDP ──→ IP ──→ 以太网 │
│ │
└─────────────────────────────────────────────────────────────────────┘

“SOME/IP是‘轻量级’——头部简单,适合控制类通信。DDS/RTPS是‘重量级’——头部复杂,但支持自动发现、可靠传输、分片等高级功能。”

RTPS的核心价值:

功能说明
性能与QoS 在标准IP网络上实现最大努力和可靠的发布-订阅通信
容错性 允许创建无单点故障的网络
即插即用 自动发现新的应用程序和服务,无需重新配置
可配置性 允许平衡每次数据交付的可靠性和及时性要求
类型安全 防止应用程序编程错误影响远程节点的操作

二、RTPS消息的整体结构

黄蓉在白板上画了RTPS消息的结构:

┌─────────────────────────────────────────────────────────────────────┐
│ RTPS消息(Message) │
├─────────────────────────────────────────────────────────────────────┤
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ 消息头(Header) │ │
│ │ 'R' 'T' 'P' 'S' │ 协议版本 │ 供应商ID │ 参与者前缀(12字节) │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ 子消息1(Submessage) │ │
│ │ ┌─────────────┬─────────────┬─────────────────────────────┐ │ │
│ │ │ 子消息ID │ 标志(flags) │ 子消息长度 │ │ │
│ │ └─────────────┴─────────────┴─────────────────────────────┘ │ │
│ │ ┌─────────────────────────────────────────────────────────┐ │ │
│ │ │ 子消息元素(SubmessageElement) │ │ │
│ │ └─────────────────────────────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ 子消息2(Submessage) │ │
│ │ … │ │
│ └───────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘

郭靖头又大了:“SOME/IP就12个字节头,这个RTPS怎么这么复杂?”

黄蓉:“SOME/IP是‘轻骑兵’,RTPS是‘重装部队’。 复杂是因为要支持自动发现、可靠传输、分片重传这些高级功能。”

三、消息头(Header)——RTPS的“身份证”

黄蓉拆开RTPS消息头:

字节偏移字段长度说明典型值
0 'R' 1字节 RTPS标识 0x52
1 'T' 1字节 RTPS标识 0x54
2 'P' 1字节 RTPS标识 0x50
3 'S' 1字节 RTPS标识 0x53
4 Protocol Version (major) 1字节 主版本号 0x02
5 Protocol Version (minor) 1字节 次版本号 0x02
6-7 Vendor ID 2字节 供应商标识 如0x01 0x00
8-19 GUID Prefix 12字节 全局唯一参与者标识

“抓包时看到52 54 50 53(RTP S),就知道这是RTPS消息,底层是DDS在通信。”

郭靖对比:“SOME/IP的‘身份证’是03 FC 80 01,DDS/RTPS的‘身份证’是52 54 50 53。各念各的经!”

四、子消息(Submessage)——RTPS的“五脏六腑”

黄蓉继续说:“RTPS消息可以有多个子消息。根据功能,子消息分为三大类:”

4.1 子消息分类

类别子消息类型作用类比
数据型 DATA、DATA_FRAG 携带用户数据 “信纸”
可靠型 HEARTBEAT、ACKNACK、GAP 可靠传输、重传机制 “回执”
信息型 INFO_TS、INFO_DST、INFO_SRC 提供时间戳、目标地址等上下文 “信封备注”

4.2 子消息头结构

每个子消息都有固定的头结构:

字节偏移字段长度说明
0 Submessage ID 1字节 标识子消息类型(如DATA=0x15)
1 Flags 1字节 标志位(字节序、是否内联QoS等)
2-3 Submessage Length 2字节 子消息长度(不含头部)

“关键点:submessageLength是2字节,最大64KB。如果要发大于64KB的数据,需要拆成多个DATA_FRAG子消息。”

五、数据子消息(DATA)——用户数据怎么装

郭靖问:“最重要的数据子消息长啥样?”

黄蓉画了DATA子消息的结构:

┌─────────────────────────────────────────────────────────────────────┐
│ DATA子消息结构 │
├─────────────────────────────────────────────────────────────────────┤
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ 子消息头(SubmessageHeader) │ │
│ │ ID=0x15 │ Flags │ Length │ │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ 读取者实体ID(readerId,4字节) │ │
│ │ 写入者实体ID(writerId,4字节) │ │
│ │ 写入者序列号(writerSN,8字节) │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ 内联QoS(可选,如果Flags中Q=1) │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ 序列化负载(serializedPayload) │ │
│ │ ├── 表示标识符(2字节) │ │
│ │ ├── 表示选项(2字节) │ │
│ │ └── 数据内容 │ │
│ └───────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘

关键字段说明:

字段说明类比
readerId/writerId 标识具体的读者和写者实体 “收件人地址”
writerSN 写入者赋予这个样本的序列号 “快递单号”,用于可靠传输
inlineQoS 内联的QoS参数(如可靠性、持久性) “加急件标记”
serializedPayload 序列化后的用户数据 “信纸”

六、可靠传输子消息——Heartbeat和AckNack

郭靖问:“SOME/IP如果丢包了怎么办?UDP不保证可靠。”

黄蓉:“SOME/IP要么靠TCP,要么应用层自己处理。DDS/RTPS在UDP之上实现了可靠传输机制。”

6.1 Heartbeat(心跳)——告诉对方“我有这些数据”

┌─────────────────────────────────────────────────────────────────────┐
│ Heartbeat子消息结构 │
├─────────────────────────────────────────────────────────────────────┤
│ 子消息头 │ 读者实体ID │ 写者实体ID │ 起始序列号 │ 结束序列号 │ 计数 │
└─────────────────────────────────────────────────────────────────────┘

作用:写入者告诉读取者:“我这里有序列号从firstSN到lastSN的数据,你需要哪些?”

6.2 AckNack(确认/否定确认)——告诉对方“我缺哪些”

┌─────────────────────────────────────────────────────────────────────┐
│ AckNack子消息结构 │
├─────────────────────────────────────────────────────────────────────┤
│ 子消息头 │ 读者实体ID │ 写者实体ID │ 读者SN状态 │ 计数 │ 标志 │
└─────────────────────────────────────────────────────────────────────┘

作用:读取者告诉写入者:“我已经收到了X、Y、Z,还缺A、B、C,请重发。”

6.3 可靠传输流程

写入者(Writer) 读取者(Reader)
│ │
│ ① Heartbeat(我有[1-10]这些数据) │
│─────────────────────────────────────────>│
│ │
│ │ 检查本地已收到[1,2,3,5,7]
│ │ 发现缺[4,6,8,9,10]
│ │
│ ② AckNack(缺[4,6,8,9,10]) │
│<─────────────────────────────────────────│
│ │
│ ③ Data(重发缺失的数据) │
│─────────────────────────────────────────>│
│ │
│ ④ Heartbeat(我再发一遍状态) │
│─────────────────────────────────────────>│
│ │
│ ⑤ AckNack(全收到了!) │
│<─────────────────────────────────────────│

郭靖恍然大悟:“哦~~这就像寄快递——我先告诉你‘我有10个包裹’,你检查收到几个,告诉我还缺哪几个,我再补发。”

七、DDS/RTPS vs SOME/IP 报文对比

黄蓉画了一张完整的对比表:

对比项SOME/IPDDS/RTPS
报文标识 03 FC 80 01(Service ID=0xFFFF时是SD) 52 54 50 53(RTP S)
头部长度 12字节固定 消息头+子消息头,可变
服务发现 SD(Offer/Find/Subscribe) 内置PDP/EDP自动发现
可靠传输 依赖UDP(不保证)或TCP 内置Heartbeat/AckNack机制
大数据传输 SOME/IP-TP(需要额外协议) 内置DATA_FRAG分片
QoS支持 无内置 23种QoS策略
适用场景 车身控制、RPC、诊断 自动驾驶、大数据分发

八、为什么先讲RTPS报文格式?

郭靖问出了关键问题:“蓉儿,为什么DDS要从RTPS报文讲起?不能先讲Topic、Publisher这些上层概念吗?”

黄蓉解释道:

“DDS和SOME/IP的学习路径不一样。SOME/IP是先有概念(Service ID、Method ID),后有报文。DDS是先有报文(RTPS),上层概念是对报文的封装。”

她画了一个学习路径对比:

SOME/IP学习路径:
概念(Service ID/Method ID)→ 报文(03 FC 80 01)→ 抓包验证

DDS学习路径:
报文(RTPS)→ 理解底层机制(发现、可靠传输、分片)→ 概念(Topic、Publisher、Subscriber)→ QoS配置

“理解了RTPS报文,你就明白了DDS为什么能自动发现、为什么能可靠传输、为什么能支持大数据分片。”

九、黄蓉的小本本

郭靖翻开她的笔记本,上面写着:

RTPS核心要点:

1. RTPS是什么:DDS的“有线协议”,让不同厂家DDS产品互操作

2. RTPS消息结构:

  • 消息头:52 54 50 53(RTP S)+ 版本 + 供应商ID + 参与者前缀

  • 子消息:DATA(数据)、HEARTBEAT(心跳)、ACKNACK(确认)、GAP(间隙)等

3. 与SOME/IP的识别方式:

  • SOME/IP:看03 FC 80 01(DoIP头部)

  • DDS/RTPS:看52 54 50 53(RTPS标识)

4. 可靠传输机制:Heartbeat告知数据范围,AckNack告知缺失,Writer补发

5. 一句口诀:RTP S开头是DDS,03FC是SOME/IP,抓包先看头字节,谁家孩子谁来认

写在最后

郭靖合上笔记本:“RTPS是DDS的底层有线协议,消息头以52 54 50 53(RTP S)开头。里面可以包含DATA(数据)、HEARTBEAT(心跳)、ACKNACK(确认)等子消息。可靠传输靠Heartbeat和AckNack配合完成。”

黄蓉咬了口糖葫芦:“报文格式讲完了。那Topic、Publisher、Subscriber这些上层概念,你现在有基础了,能理解它们是怎么在RTPS报文里体现的吗?”

郭靖若有所思:“DATA子消息里装的serializedPayload,就是Topic的数据?”

黄蓉点头:“对!下篇就讲——Topic怎么定义、用IDL描述、和RTPS报文怎么对应。”

打完收工,886。

赞(0)
未经允许不得转载:171主机测评 » 车载以太网之要火系列 - 第51篇郭大侠学DDS(RTPS报文):底层报文长啥样 一看报头不一样
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址