车载以太网之要火系列 - 第61篇郭大侠学DDS(一发多收):广播只是牵线,单播才是常态
2026/6/4 12:58:54 网站建设 项目流程

写在开篇·蓉儿又挖坑

上回说到,郭靖学完了DDS的完整体系——从Topic到QoS,从RTPS到发现阶段,从发布订阅到与SOME/IP分工。

郭靖合上笔记本,突然皱起眉头:“蓉儿,我想到一个问题——你之前说DDS是一发多收,摄像头发一次数据,域控、座舱、记录仪都能收到。但你又说过,发现阶段用的是广播,匹配成功后业务数据走单播。”

单播是点对点的,一个摄像头怎么同时给三个接收方发单播?那岂不是要发三份?那还叫一发多收吗?

黄蓉咬了口糖葫芦:“问得好!这个问题,很多人学DDS都会卡在这里。今天就把‘一发多收’的实现原理讲清楚——广播只是牵线,单播才是常态,但‘一发’是应用层的一发,不是网络层的一发。”

一、问题的核心:一发多收 vs 单播

郭靖画了一张图,标注出自己的困惑:

┌─────────────────────────────────────────────────────────────────────┐ │ 郭靖的困惑 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ 说的“一发多收”: │ │ ┌─────────┐ │ │ │ 摄像头 │ ── 发一次数据 ──→ 域控、座舱、记录仪都能收到 │ │ └─────────┘ │ │ │ │ 但单播的定义: │ │ ┌─────────┐ ┌─────────┐ │ │ │ 摄像头 │ ──── 单播 ────→ │ 域控 │ │ │ └─────────┘ └─────────┘ │ │ ┌─────────┐ ┌─────────┐ │ │ │ 摄像头 │ ──── 另一个单播 ────→ │ 座舱 │ │ │ └─────────┘ └─────────┘ │ │ ┌─────────┐ ┌─────────┐ │ │ │ 摄像头 │ ──── 又一个单播 ────→ │ 记录仪 │ │ │ └─────────┘ └─────────┘ │ │ │ │ 这不就是发了三次吗?怎么叫“一发多收”? │ │ │ └─────────────────────────────────────────────────────────────────────┘

二、关键区分:应用层的一发 vs 网络层的多发

黄蓉在白板上写下核心答案:

“一发多收”是应用层的概念——应用程序只调用了一次write()
底层DDS协议栈针对每个订阅者,分别发送了一份数据——网络层发了多份。

郭靖追问:“那这不就是骗人吗?应用层一发,网络层多发,本质上还是发了多次啊。”

黄蓉摇头:“不是骗人,是‘让应用层不用操心’。如果没有DDS,应用层要自己管理订阅者列表,自己给每个订阅者发一份,还要处理谁在线、谁离线、谁要重传。”

对比:

方式应用层要做什么网络层发了几次
没有DDS维护订阅者列表、循环发送、处理重传N次(应用层自己发的)
有DDS调用一次write()N次(DDS协议栈帮你发的)

DDS的价值:你只写一次,剩下的DDS帮你搞定。你不用关心有几个订阅者、他们是谁、在哪、要不要重传。

三、完整时序图:从广播到单播

黄蓉画了一张完整的时序图,展示从发现到数据传输的全过程:

┌─────────────────────────────────────────────────────────────────────────────┐ │ DDS“一发多收”完整时序图(摄像头 → 三个订阅者) │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ 摄像头(发布者) 域控(订阅者) 座舱(订阅者) 记录仪(订阅者) │ │ │ │ │ │ │ │ │ ========== 发现阶段(广播) ========== │ │ │ │ │ │ │ │ │ │ ① SPDP/SEDP广播(“我要发/camera/front”) │ │ │ │──────────────────────────────────────────────────────>│ │ │ │ │ │ │ │ │ │ │ ② 各自广播自己要收/camera/front │ │ │ │<─────────────────│<────────────────│<────────────────│ │ │ │ │ │ │ │ │ │ ========== 匹配成功,建立连接 ========== │ │ │ │ │ │ │ │ │ │ ③ 单播握手 │ │ │ │ │ │<─────────────────>│ │ │ │ │ │ ④ 单播握手 │ │ │ │ │<──────────────────────────────────>│ │ │ │ │ ⑤ 单播握手 │ │ │ │<────────────────────────────────────────────────────>│ │ │ │ │ │ │ │ │ │ ========== 数据传输(单播) ========== │ │ │ │ │ │ │ │ │ │ ⑥ 应用层调用一次write()(一发) │ │ │ │ │ ┌─────────────────────────────────────────────┐ │ │ │ │ │ DDS协议栈自动复制数据,发给每个订阅者 │ │ │ │ │ └─────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ │ │ ⑦ DATA(单播) │ │ │ │ │ │─────────────────>│ │ │ │ │ │ ⑧ DATA(单播) │ │ │ │ │──────────────────────────────────>│ │ │ │ │ ⑨ DATA(单播) │ │ │ │────────────────────────────────────────────────────>│ │ │ │ │ │ │ │ │ │ ⑩ 各订阅者回复AckNack(如果需要) │ │ │ │ │<─────────────────│ │ │ │ │ │<──────────────────────────────────│ │ │ │ │<────────────────────────────────────────────────────│ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘

四、为什么不能一直用广播?

郭靖问:“既然要发给多个人,为什么不直接用广播?广播一次,所有人都能收到,不是更省事?”

黄蓉解释:

问题说明
网络负载广播会被交换机转发到所有端口,浪费带宽。单播只发给需要的接收方
安全性广播谁都能收,不该收的人也收到了。单播只发给订阅者
可靠性广播没有确认机制,丢了不知道。单播可以配合RELIABLE做确认重传
选择性广播无法选择接收方。单播可以只发给订阅了的人

广播适合“喊一嗓子让大家知道”,不适合“天天传大量数据”。

五、DDS的“一发多收” vs IP组播

郭靖问:“那IP组播呢?一次发送,多个接收,不也是‘一发多收’吗?DDS为什么不直接用组播?”

黄蓉画了对比表:

对比项DDS单播IP组播
网络支持所有交换机都支持需要交换机开启IGMP Snooping,配置复杂
可靠性可配置RELIABLE,有确认重传UDP组播不可靠,丢包不重传
QoS支持优先级、延迟预算等
订阅机制基于Topic,应用层自动匹配基于组播组地址,需要手动配置
适用范围任意网络需要网络设备支持

DDS设计选择单播,是为了简化网络配置,同时保证可靠性和QoS。组播虽然网络层“一发多收”,但牺牲了可靠性和灵活性。

六、应用层的一发,DDS帮你发了N份

黄蓉画了一个对比图,解释DDS的“一发”到底是什么意思:

┌─────────────────────────────────────────────────────────────────────┐ │ 没有DDS:应用层自己管多份 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ 应用层代码: │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ for each subscriber in subscriber_list: │ │ │ │ send_to(subscriber, data) │ │ │ │ wait_for_ack() │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ │ 问题:订阅者列表要自己维护,上线/下线要自己处理,重传要自己写 │ │ │ └─────────────────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────────────┐ │ 有DDS:应用层只管发一次 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ 应用层代码: │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ datawriter.write(data); // 就这一行! │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ DDS协议栈自动: │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ - 查询匹配的订阅者列表 │ │ │ │ - 为每个订阅者序列化数据 │ │ │ │ - 为每个订阅者单独发送 │ │ │ │ - 处理确认和重传 │ │ │ │ - 处理新订阅者加入 │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────┘

七、新订阅者加入怎么办

郭靖问:“如果数据已经在发了,突然一个新订阅者加入(比如座舱开机晚了),它能收到之前的数据吗?”

黄蓉解释:

持久性QoS行为
VOLATILE(易失)只收订阅之后的数据,之前的不补
TRANSIENT_LOCAL(临时持久)DDS在本地缓存历史数据,新订阅者加入时推送最近N条

这不影响“一发多收”的机制。新订阅者加入后,DDS会把它加入发送列表,后续数据自动发给它。

八、黄蓉的小本本

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

DDS“一发多收”的本质:

1. 应用层视角:你只调用了一次write(),剩下的不用管

2. 网络层视角:DDS协议栈给每个订阅者单独发送了一份(单播)

3. 不是魔法,是DDS帮你做了“循环发送”

4. 发现阶段用广播(牵线),数据传输用单播(干活)

5. 为什么不用IP组播?因为单播更简单、更可靠、更灵活

6. 一句话:“一发”是应用层的一发,“多收”是DDS帮你送了多份。

写在最后

郭靖合上笔记本:“我终于明白了。‘一发多收’不是网络层的一次发送,多方接收;而是应用层只调用一次,DDS协议栈给每个订阅者单独发送。广播只是发现阶段牵线用的,真正的数据传输是点对点的单播。DDS帮我做了‘循环发送’的脏活累活。”

黄蓉咬了口糖葫芦:“全明白了?”

郭靖点头:“明白了。应用层只管发一次,剩下的DDS帮我搞定。

黄蓉眨眨眼:“那下一步,TSN——让数据准时到达?”

郭靖憨笑:“先歇歇,回头再战。

打完收工,886。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询