写在开篇·蓉儿又挖坑上回说到郭靖学完了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。