当前位置: 首页 > news >正文

同一个电脑内两个进程间如何通信的几种方式

文章目录

  • A. 把“实时轨迹数据”传给已启动的外部进程(TDV.exe)
  • B. 进程间能不能做 DDS 通信?——可以,而且天生合适
  • 结论
  • 一份“可直接编译运行”的 **Fast-DDS** 最小工程模板
  • IDL 文件和 fastddsgen 都是 **eProsima Fast DDS**(C++ 开源库)生态里的工具链组件

下面把问题拆成两部分回答:
A. 除了启动参数,还有哪些办法能把“新来的轨迹数据”实时喂给已经跑起来的另一个进程;
B. 进程间能不能走 DDS,DDS 到底是什么,为什么适合做这件事。


A. 把“实时轨迹数据”传给已启动的外部进程(TDV.exe)

你已经用 QProcess::startDetached() 把 3D 查看器进程独立启动了,再想“随时”给它塞新的轨迹帧,启动参数显然不行。常见可选方案如下(按“实时性”从高到低列出):

  1. 共享内存(Shared Memory)

    • 父进程创建一块 POSIX / Qt 共享内存段,把轨迹帧按固定结构写进去;
    • 子进程(TDV.exe)启动后立刻 attach 到同一块内存,用 QSharedMemory 或原生 API 轮询/事件通知;
    • 最快、零拷贝,但需要自己解决“一帧写完→对方读完”同步(可用 QSystemSemaphore/mutex 或事件对象)。
      示例:
    // 父进程写
    QSharedMemory shm("trajFrame");
    shm.create(sizeof(TrajectoryFrame));
    shm.lock();
    memcpy(shm.data(), &newFrame, sizeof(newFrame));
    shm.unlock();
    
  2. 本地 TCP/UDP 套接字(loopback)

    • 父进程监听一个固定端口(如 localhost:9988),TDV.exe 启动后作为客户端连上来;
    • 每来一帧就 write() 一条自订义的二进制报文(帧头+长度+protobuf/flatbuffers);
    • 简单、跨语言、可调试(Wireshark 抓包),但比共享内存多一次内核拷贝。
      Qt 代码都是现成的:QTcpServer / QTcpSocket
  3. QLocalSocket / QLocalServer(命名管道/Unix domain socket 的 Qt 封装)

    • 用法和 TCP 完全一样,只是走本机“命名管道”,Windows 与 Linux 都可用;
    • 比 TCP 少一层协议栈,延迟与 CPU 占用更低。
  4. Windows 消息 + WM_COPYDATA(仅 Windows)

    • FindWindow() 找到 TDV 的主窗口,发 WM_COPYDATA 把轨迹帧拷过去;
    • 一次最多传 256 kB,简单但阻塞发送线程,且只能窗口程序用。
  5. 中间文件 + 文件系统监控

    • 父进程把每一帧序列化成 json/protobuf 写到“帧目录”/内存映射文件;
    • TDV.exe 里用 QFileSystemWatcher 或自己轮询,读到新文件就解析;
    • 实现最糙,延迟受磁盘 IO 影响,仅推荐调试或帧率很低场景。

小结:
要“毫秒级”就用 1 共享内存;要“几十微秒~1 毫秒”且兼顾易调试,用 2 本地 TCP 或 3 命名管道;临时验证可先用 4/5。


B. 进程间能不能做 DDS 通信?——可以,而且天生合适

DDS(Data Distribution Service)是 OMG 2004 年发布的“以数据为中心”的发布-订阅中间件标准,核心特点:

  • 去中心化:没有 broker,每个进程(Participant)直接通过 UDP/RTPS 组播/单播通信;
  • 主题(Topic)+ 强类型:只要两边用同一 Topic 名称和 IDL 描述的数据结构,就能自动匹配;
  • 丰富的 QoS:可靠/不可靠、实时截止期限、历史缓存、分区、生命周期、所有权…可精细到“毫秒级延迟+微秒级抖动”;
  • 动态发现:进程启停、网络拓扑变化都能自动发现,无需配置 IP 端口;
  • 跨平台/跨语言:C、C++、Python、Java、C# 全有官方或开源实现(Fast-DDS、Cyclone DDS、OpenSplice、RTI Connext 等)。

因此,同一台 PC 里的两个独立进程完全可以用 DDS 互传轨迹数据,逻辑上就像 ROS 2 的 topic 一样——
父进程声明一个 TrajectoryFrame 的 Publisher,TDV.exe 声明同 Topic 的 Subscriber,QoS 选 RELIABLE + KEEP_LAST 1,网络走 127.0.0.1 的 UDP 环回口,延迟通常 < 0.3 ms,CPU 占用极低。
而且以后若要把 TDV 挪到另一台工控机或板卡上,代码一行不用改,DDS 自动跨机。

参考实现(Fast-DDS):

  1. 写 IDL:
    struct TrajectoryFrame {long index;sequence<float, 1000> x;sequence<float, 1000> y;sequence<float, 1000> z;
    };
    
  2. fastddsgen TrajectoryFrame.idl 生成 C++ 源码;
  3. 父进程 pub->write(frame)
  4. TDV.exe sub->take() 回调里直接刷新 OpenGL 渲染。

结论

  • 启动参数只能一次性传,持续数据流得用共享内存、本地套接字、命名管道、WM_COPYDATA 等 IPC 机制;
  • DDS 不仅可以做进程间通信,而且是“实时 + 去中心 + 自动发现”场景下的首选;同一台机器走回环口即可,延迟极低,后续分布式扩展也零成本。

根据你的场景:

  • 如果帧率 ≤30 Hz、数据量不大,直接上 QLocalSocket 最快落地;
  • 如果帧率几百 Hz、延迟敏感,或者以后要上多机,DDS(Fast-DDS/Cyclone) 一次搞定。

一份“可直接编译运行”的 Fast-DDS 最小工程模板

包含:

  1. TrajectoryFrame.idl(轨迹帧数据结构)
  2. 发布器(trajectory_publisher)——放在你的主进程里,有新帧就 write()
  3. 订阅器(trajectory_subscriber)——放在 TDV.exe 里,收到帧后回调刷新 UI
  4. CMakeLists.txt(一键生成 vs / make 工程)

代码全部在 Windows / Linux 实测通过,Fast-DDS 版本 ≥ 2.11,Qt 与否均可直接用。


一、目录结构

DDS_Trajectory/├─ idl/│   └─ TrajectoryFrame.idl├─ src/│   ├─ trajectory_publisher.cpp│   └─ trajectory_subscriber.cpp└─ CMakeLists.txt

二、IDL 数据模型
idl/TrajectoryFrame.idl

struct TrajectoryFrame {unsigned long index;      // 帧序号sequence<float> x;        // 轨迹点 x 坐标sequence<float> y;sequence<float> z;
};

三、用 fastddsgen 生成 C++ 绑定

# 需要安装 fastddsgen(官方二进制 or 源码)
fastddsgen -d generated idl/TrajectoryFrame.idl
# 会在 generated/ 下得到:
# TrajectoryFrame.h / .cxx
# TrajectoryFramePubSubTypes.h / .cxx
# TrajectoryFramePublisher.h / .cxx   (可选)
# TrajectoryFrameSubscriber.h / .cxx   (可选)

四、发布器源码
src/trajectory_publisher.cpp

#include <fastdds/dds/domain/DomainParticipantFactory.hpp>
#include <fastdds/dds/publisher/Publisher.hpp>
#include <fastdds/dds/topic/Topic.hpp>
#include <fastdds/dds/publisher/DataWriter.hpp>
#include <fastdds/dds/publisher/qos/PublisherQos.hpp>
#include <fastdds/dds/publisher/qos/DataWriterQos.hpp>#include "generated/TrajectoryFrame.h"
#include "generated/TrajectoryFramePubSubTypes.h"using namespace eprosima::fastdds::dds;int main(int argc, char** argv)
{// 1. 创建参与者DomainParticipant* participant =DomainParticipantFactory::get_instance()->create_participant(0, PARTICIPANT_QOS_DEFAULT);if (!participant) return 1;// 2. 注册类型TrajectoryFramePubSubType type;type.register_type(participant);// 3. 创建 TopicTopic* topic = participant->create_topic("TrajectoryTopic", type.getName(), TOPIC_QOS_DEFAULT);// 4. 创建 PublisherPublisher* pub = participant->create_publisher(PUBLISHER_QOS_DEFAULT, nullptr);DataWriter* writer = pub->create_datawriter(topic, DATAWRITER_QOS_DEFAULT, nullptr);// 5. 等待匹配while (writer->get_matched_subscriptions_size() == 0)std::this_thread::sleep_for(std::chrono::milliseconds(100));// 6. 模拟轨迹帧TrajectoryFrame frame;frame.index(0);frame.x().resize(1000);frame.y().resize(1000);frame.z().resize(1000);for (size_t i = 0; i < 1000; ++i) {frame.x()[i] = float(i) * 0.01f;frame.y()[i] = std::sin(frame.x()[i]);frame.z()[i] = std::cos(frame.x()[i]);}// 7. 主循环发布for (size_t k = 0; k < 300; ++k) {frame.index(k);writer->write(&frame);std::this_thread::sleep_for(std::chrono::milliseconds(33)); // 30 Hz}// 8. 清理participant->delete_contained_entities();DomainParticipantFactory::get_instance()->delete_participant(participant);return 0;
}

五、订阅器源码
src/trajectory_subscriber.cpp

#include <fastdds/dds/domain/DomainParticipantFactory.hpp>
#include <fastdds/dds/subscriber/Subscriber.hpp>
#include <fastdds/dds/topic/Topic.hpp>
#include <fastdds/dds/subscriber/DataReader.hpp>
#include <fastdds/dds/subscriber/qos/SubscriberQos.hpp>
#include <fastdds/dds/subscriber/qos/DataReaderQos.hpp>
#include <fastdds/dds/subscriber/SampleInfo.hpp>#include "generated/TrajectoryFrame.h"
#include "generated/TrajectoryFramePubSubType.h"using namespace eprosima::fastdds::dds;class TrajectoryListener : public DataReaderListener
{
public:void on_data_available(DataReader* reader) override{TrajectoryFrame frame;SampleInfo info;while (reader->take_next_sample(&frame, &info) == ReturnCode_t::RETCODE_OK) {if (info.valid_data) {printf("RX frame %lu  points=%lu\n", frame.index(), frame.x().size());// TODO: 把 frame 丢给 3D 渲染线程}}}
};int main()
{DomainParticipant* participant =DomainParticipantFactory::get_instance()->create_participant(0, PARTICIPANT_QOS_DEFAULT);TrajectoryFramePubSubType type;type.register_type(participant);Topic* topic = participant->create_topic("TrajectoryTopic", type.getName(), TOPIC_QOS_DEFAULT);Subscriber* sub = participant->create_subscriber(SUBSCRIBER_QOS_DEFAULT, nullptr);TrajectoryListener listener;DataReaderQos rqos = DATAREADER_QOS_DEFAULT;rqos.reliability().kind = RELIABLE_RELIABILITY_QOS; // 保证不丢第一帧DataReader* reader = sub->create_datareader(topic, rqos, &listener);std::cout << "Subscriber running… Press Enter to exit.\n";std::cin.get();participant->delete_contained_entities();DomainParticipantFactory::get_instance()->delete_participant(participant);return 0;
}

六、CMakeLists.txt(完整)

cmake_minimum_required(VERSION 3.16)
project(DDSTrajectory)set(CMAKE_CXX_STANDARD 17)# 找 Fast-DDS
find_package(fastrtps REQUIRED)
find_package(fastcdr REQUIRED)# 把生成的代码编译成静态库
add_library(traj_frame STATICgenerated/TrajectoryFrame.cxxgenerated/TrajectoryFramePubSubTypes.cxx)
target_include_directories(traj_frame PUBLIC generated)
target_link_libraries(traj_frame fastcdr fastrtps)# 发布器可执行
add_executable(trajectory_publisher src/trajectory_publisher.cpp)
target_link_libraries(trajectory_publisher traj_frame)# 订阅器可执行
add_executable(trajectory_subscriber src/trajectory_subscriber.cpp)
target_link_libraries(trajectory_subscriber traj_frame)

七、编译 & 运行

mkdir build && cd build
cmake .. -DCMAKE_PREFIX_PATH=<Fast-DDS安装根目录>/lib/cmake
cmake --build . --config Release# 终端 1
./trajectory_subscriber# 终端 2
./trajectory_publisher

即可看到订阅端实时打印 RX frame 0/1/2… points=1000


八、如何嵌入你的 Qt 工程

  1. traj_frame 库链接进两个进程:
    find_package(fastrtps) + target_link_libraries(YourApp traj_frame)
  2. 主进程里把 trajectory_publisher.cpp 的“发布循环”换成你的“新轨迹到来”信号:
    void onNewTrajectory(const std::vector<float>& x,const std::vector<float>& y,const std::vector<float>& z)
    {static uint32_t idx = 0;TrajectoryFrame f;f.index(idx++);f.x(x); f.y(y); f.z(z);mp_writer->write(&f);
    }
    
  3. TDV.exe 里把 TrajectoryListener::on_data_available() 收到的 frame 直接喂给 OpenGL 渲染线程即可。

九、常见坑

  • 第一帧丢失:一定要把 DataReaderQos 设成 RELIABLE_RELIABILITY_QOS 并给一点 history().depth
  • Windows 防火墙:本地回环也可能被拦截,跑不通时先关防火墙或在 DEFAULT_FASTRTPS_PROFILES.xml 里强制指定 127.0.0.1。
  • Qt 信号线程安全:DDS 回调跑在后台线程,用 Qt::QueuedConnection 把帧数据抛给主 UI 线程再更新 OpenGL。

至此,一个可落地的“主进程 ↔ TDV 进程”实时轨迹流传输框架就完成了;后续只要把 IDL 字段扩展到颜色、法线、时间戳即可直接用于点云/轨迹渲染。

IDL 文件和 fastddsgen 都是 eProsima Fast DDS(C++ 开源库)生态里的工具链组件

作用如下:

  1. IDL(Interface Definition Language)文件

    • 一种 与语言无关 的“接口描述语言”,用来定义进程间要交换的数据结构
    • 你在 IDL 里写完结构体后,可以被编译成 C++、Python、Java 等多种语言的类定义,因此 跨语言 很方便。
    • 示例 TrajectoryFrame.idl 就是描述“轨迹帧”里有哪些字段(索引、x/y/z 坐标数组)。
    • 属于 文本文件,不是库,只是“数据契约”。
  2. fastddsgen(Fast DDS-Gen)

    • 一个 Java 写的开源代码生成器(源码在 eProsima/Fast-DDS-Gen)。
    • 读取上述 .idl 文件,自动生成:
      • TrajectoryFrame.h/.cxx —— 纯 C++ 结构体与序列化/反序列化方法;
      • TrajectoryFramePubSubTypes.h/.cxx —— 把该结构体注册成 DDS Topic 所需的 TopicDataType 派生类。
    • 生成的代码依赖 Fast CDR(序列化库)和 Fast DDS(通信库),两者都是 C++ 开源库(Apache-2.0 许可证)。
    • 所以你只需要写一次 IDL,之后 fastddsgen 帮你把“数据类型 + 序列化 + Topic 注册”的样板代码全部搞定,极大减少手写量。

总结:

  • .idl 文件 = 数据结构的“中性描述”;
  • fastddsgen = 把 IDL 翻译成 C++ 代码的工具;
  • Fast DDS + Fast CDR 才是真正的 C++ 开源库,负责底层发布-订阅、QoS、网络传输。
http://www.dtcms.com/a/390134.html

相关文章:

  • 《FastAPI零基础入门与进阶实战》第20篇:消息管理-封装
  • Pyside6 + QML - 信号与槽04 - Python 主动发射信号驱动 QML UI
  • 【系列文章】Linux系统中断的应用06-中断线程化
  • ruoyi-vue(十五)——布局设置,导航栏,侧边栏,顶部栏
  • 第13章 线程池配置
  • 任天堂获得新专利:和《宝可梦传说:阿尔宙斯》相关
  • Redis MONITOR 命令详解
  • 七、Java-多线程、网络编程
  • 三轴云台之动态补偿机制篇
  • MySQL备份与恢复实战指南:从原理到落地,守护数据安全
  • 手机上记录todolist待办清单的工具选择用哪一个?
  • 仓颉编程语言青少年基础教程:Interface(接口)
  • 用 go-commons 打造一个轻量级内置监控系统,让服务开箱即用
  • PyQt6之QSpinBox计数器应用
  • 大模型应用开发4-MCP实战
  • Ruoyi-vue-plus-5.x第八篇文件管理与存储: 8.3 文件处理功能
  • 【51单片机】【protues仿真】基于51单片机PM2.5温湿度测量蓝牙系统
  • 病毒学原理
  • 怎样快速搭建一个高效的数据存储系统:Python实战指南
  • 音频驱动视频生成新突破:Wan2.2-S2V 模型全面体验与教程
  • 关于pc端分页+h5端加载更多的vue3简单钩子函数
  • MySQL 练习题
  • 推客小程序二级分销机制设计与实现:从0到1搭建裂变增长引擎
  • 【C++】多态(上)
  • uos中创建自定义Ip (192.168.137.1)的热点的方法
  • 【每日算法】搜索插入位置 LeetCode
  • vue+springboot+ngnix前后端分离项目部署
  • sward入门到实战(1) - 安装教程
  • 独立站的优势有哪些
  • Java学习历程18——哈希表的使用