qt Qt Remote Object(QtRO)实现进程间通信

简介

Qt Remote Object简称QtRO,这是Qt5.9以后官方推出来的新模块,专门用于进程间通信(IPC)。是基于Socket来封装的,兼容LPC和RPC。LPC即Local Process Communication,而RPC是指Remote Process Communication,两者都属于IPC。如果用于LPC,则QtRO使用QLocalSocket;如果是用于RPC,则使用QTcpSocket。

它最大的特点是使得远端通信能与本机通信一样使用信号槽的方式来收发信息。

每个进程通过QRemoteObjectNode接入QtRO网络。功能提供节点(可以理解为服务器)需要使用QRemoteObjectHost将一个提供实际功能的QObject派生类注册进QtRO网络中,然后其他使用该功能的程序则通过各自的QRemoteObjectNode连接到该Host上,然后acquire一个该功能对象的Replica。等到该Replica初始化好后,该程序就能够使用Replica中的信号、槽以及属性,就好像功能类就在本地一样。

优点:
  1. 使用 QtRO 则天生支持Qt 自带的类型,如 QString 、QByteArray 。
  2. 而且定义的接口支持信号槽。
缺点:

host 不能直接访问当前连接的 node,服务端是所有已连接的 node 共享的,如果 host-source 发信号,那么所有连接的 node 都会收到这个信号。从这点来看 QtRO 更适合单个客户端的进程交互,不适合多个客户端的并发访问,多个客户端时要独立操作则不该使用信号,可以通过槽函数返回值来返回结果。

关键步骤

要使用QtRO有几个关键步骤,我们暂且将两个端分为Server和Client。

Server端需要把功能类通过QRemoteObjectHost的enableRemoting方法共享出来

Client连接到该QRemoteObjectHost,然后acquire到Replica

QtRO会自动初始化该Replica,待初始化完后客户端就可以用该Replica。

QtRO支持的参数类型

QtRO可以收发的数据类型由rep文件中定义的信号和槽决定的,QRO允许发送的信号参数类型包括以下几种:

1.基本数据类型:如int、bool、char、float、double等。

2.Qt的核心类:如QString、QList、QMap等。

3.Qt的自定义类:只要这些类实现了序列化功能,就可以作为信号参数。

使用QtRO编写服务端
创建rep文件

rep文件是一种DSL(Domain Specific Language),专门用于定义QtRO接口。在编译的时候,该文件会首先经过repc.exe这个程序处理,生成对应的头文件和源文件。只要安装Qt时选择了Qt RemoteObjects模块,repc.exe就在Qt安装目录的bin目录中。

如 rep文件

#include <QObject>#include <QString>POD VarInfo(QString varName,QString value,);ENUM PlatType{P_Unknown = -1,       //未知平台P_Firm = 0,P_Fit = 1,P_Speed = 2};class CommonInterface{SIGNAL(sigMessage(VarInfo msg));   //server下发消息给clientSLOT(bool onMessage(QString msg,PlatTypeEnum::PlatType type)); //server接收client的消息PROP(QString strname);         // Property}

Rep 文件的介绍见:Qt Remote Objects Compiler | Qt Remote Objects 5.15.16

PROP

Q_PROPERTY元素是通过在rep文件中使用PROP关键字创建的。语法是PROP关键字后跟括号中的定义,其中定义是类型、名称和(可选的)默认值或属性。

 PROP(QString strname);  

CLASS

CLASS关键字为从QObject派生的对象生成特殊的Q_PROPERTY元素。这些属性与SOURCEONLYSETTER具有相同的语义。语法是CLASS关键字后跟属性名,然后是括在括号中的子对象类型。

Signal

Signal方法是通过使用rep文件中的SIGNAL关键字创建的。 用法是声明SIGNAL,后跟用括号括起来的所需签名。应该跳过void返回值。

SIGNAL(sigMessage(VarInfo msg));   

SLOT

插槽方法是使用rep文件中的Slot关键字创建的。 用法是声明SLOT,后跟用括号括起来的所需签名。返回值可以包含在声明中。如果返回值被跳过,将在生成的文件中使用void。

SLOT(bool onMessage(QString msg,PlatTypeEnum::PlatType type));

ENUM

枚举(在QtRO中使用C++ enum和Qt的Q_enum的组合)是使用ENUM关键字描述的。

ENUM PlatType{

    P_Unknown = -1,       //未知平台

    P_Firm = 0,

    P_Fit = 1,

    P_Speed = 2

};

POD

Plain Old Data 普通旧数据(POD)是一个描述简单数据集合的术语,类似于C++结构.即自定义结果类型。

POD VarInfo(

   QString varName,

   QString value,

   );

REPC_SOURCE

指定项目中用于生成源文件的所有表示文件的名称。即用在服务端

REPC_SOURCE += \

../Reps/commoninterface.rep

REPC_REPLICA

指定项目中用于生成副本头文件的所有rep文件的名称,即用在客户端。

REPC_REPLICA += \

        ../Reps/commoninterface.rep

配置rep文件

在server端的pro文件中将rep文件添加进来

REPC_SOURCE += \

../Reps/commoninterface.rep

接着添加QtRO模块

QT  += remoteobjects

直接qmake,编译,这时候repc.exe会将rep文件生成对应的头文件,在程序输出目录下可以找到.

打开文件如下图:

继承实现功能

继承自动生成的这个CommonInterfaceSimpleSource类,并且实现其中的所有纯虚函数。如果没有 加上PROP(QString strname);  就不会生成CommonInterfaceSimpleSource类,直接继承CommonInterfaceSource类。

头文件

#ifndef COMMONINTERFACE_H#define COMMONINTERFACE_H#include "rep_commoninterface_source.h"   //在这里引用的是debug目录下编译的rep_commoninterface_source.hclass CommonInterface : public CommonInterfaceSimpleSource{Q_OBJECTpublic:explicit CommonInterface(QObject * parent = nullptr);//这个就是rep文件设置,接收数据的虚函数virtual bool onMessage(QString msg, PlatTypeEnum::PlatType type);void senddata(QString msg);signals:void sigReceiveMsg(QString msg);//把从客户端接收到的数据发送到界面上};#endif // COMMONINTERFACE_H

源文件

#include "CommonInterface.h"CommonInterface::CommonInterface(QObject *parent){}bool CommonInterface::onMessage(QString msg, PlatTypeEnum::PlatType type){if(type == PlatTypeEnum::PlatType::P_Firm){emit sigReceiveMsg(msg);}return true;}void CommonInterface::senddata(QString msg){VarInfo info;info.setValue(msg);info.setVarName("1");emit sigMessage(info);}
初始化QtRO并调用

头文件

#ifndef DIALOG_H#define DIALOG_H#include <QDialog>#include "CommonInterface.h"QT_BEGIN_NAMESPACEnamespace Ui { class Dialog; }QT_END_NAMESPACEclass Dialog : public QDialog{Q_OBJECTpublic:Dialog(QWidget *parent = nullptr);~Dialog();void init();private slots:void onReceiveMsg(QString msg);void on_pushButton_clicked();private:Ui::Dialog *ui;CommonInterface * m_pInterface ;QRemoteObjectHost * m_pHost ;};#endif // DIALOG_H

源文件

#include "dialog.h"#include "ui_dialog.h"Dialog::Dialog(QWidget *parent): QDialog(parent), ui(new Ui::Dialog){init();ui->setupUi(this);setWindowTitle("Server");}Dialog::~Dialog(){delete ui;}void Dialog::init(){m_pHost = new QRemoteObjectHost(this);m_pHost->setHostUrl(QUrl("local:interfaces"));m_pInterface = new CommonInterface(this);m_pHost->enableRemoting(m_pInterface);connect(m_pInterface,&CommonInterface::sigReceiveMsg,this,&Dialog::onReceiveMsg);}void Dialog::onReceiveMsg(QString msg){ui->textEdit->clear();ui->textEdit->setText(msg);}void Dialog::on_pushButton_clicked(){QString text = ui->lineEdit->text();m_pInterface->senddata(text);}

这里是本机中不同进程的通信,可以HostURL中字符串格式为"local:xxxx",其中xxxx必须是唯一的字符串,不同和其他程序有冲突,否则将会无法连接。

使用QtRO编写客户端
配置rep文件

Client端和Server必须共用同一个rep文件,在工程文件pro中添加

REPC_REPLICA += \

    ../Reps/commoninterface.rep

添加QtRO模块

QT       += remoteobjects

client添加完rep过后,直接编译,然后会在输出目录生成一个文件

打开文件:

初始化QtRO并调用

和server端不同的是,client端不需要重新实现功能类。直接初始化并调用即可。

头文件

#ifndef DIALOG_H#define DIALOG_H#include <QDialog>#include "rep_CommonInterface_replica.h"QT_BEGIN_NAMESPACEnamespace Ui { class Dialog; }QT_END_NAMESPACEclass Dialog : public QDialog{Q_OBJECTpublic:Dialog(QWidget *parent = nullptr);~Dialog();private:void init();private slots:void on_pushButton_clicked();void onReceiveMsg(VarInfo msg);private:Ui::Dialog *ui;QRemoteObjectNode * m_pRemoteNode ;CommonInterfaceReplica * m_pInterface ;};#endif // DIALOG_H

源文件

#include "dialog.h"#include "ui_dialog.h"Dialog::Dialog(QWidget *parent): QDialog(parent), ui(new Ui::Dialog){init();ui->setupUi(this);setWindowTitle("Client");}Dialog::~Dialog(){delete ui;}void Dialog::init(){m_pRemoteNode = new QRemoteObjectNode(this);m_pRemoteNode->connectToNode(QUrl("local:interfaces"));m_pInterface = m_pRemoteNode->acquire<CommonInterfaceReplica>();connect(m_pInterface,&CommonInterfaceReplica::sigMessage,this,&Dialog::onReceiveMsg);}void Dialog::on_pushButton_clicked(){QString msg = ui->lineEdit->text();QRemoteObjectPendingReply<bool>  ret =  m_pInterface->onMessage(msg,PlatTypeEnum::PlatType::P_Firm); //异步调用槽发送消息给服务器bool bret = ret.waitForFinished();//等待函数if(bret){bool bval = ret.returnValue();ui->lineEdit->clear();}else{QString err = "超时";}}void Dialog::onReceiveMsg(VarInfo msg){ui->textEdit->clear();ui->textEdit->setText(msg.varName() + ":" + msg.value());}

注意:在客户端调用服务端的槽函数(也是虚函数,通过rep文件设置),属于异步调用。如果该槽函数有返回值的,一般要通过等待函数waitForFinished,并通过QRemoteObjectPendingReply类的returnValue获取返回值。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/285107.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

电子电器架构 —— 诊断数据DTC具体故障篇

电子电器架构 —— 诊断数据DTC起始篇 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师 (Wechat:gongkenan2013)。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 本就是小人物,输了就是输了,不要在意别人怎么看自己。江湖一碗茶,喝完再挣扎…

【Postman】工具使用介绍

一、postman工具介绍 1.什么是postman postman是谷歌开发的一款网页调试和接口测试工具&#xff0c;能够发送任何请求类型的http请求&#xff0c;支持GET/POST/PUT/DELETE等方法。postman简单易用&#xff0c;可以直接填写URL&#xff0c;header&#xff0c;body就可以发送一…

二次开发Flink-coGroup算子支持迟到数据通过测输出流提取

1.背景 coGroup算子开窗到时间关闭之后&#xff0c;迟到数据无法通过测输出流提取&#xff0c;intervalJoin算子提供了api&#xff0c;因为join算子底层就是coGroup算子&#xff0c;所以Join算子也不行。 flink版本 v1.17.1 2.coGroup算子源码分析 2.1完成的coGroup算子调用流…

SpringCloud中网关实现笔记

SpringCloud中网关实现笔记 SpringCloudGateway&#xff1a;基于Spring的WebFlux技术&#xff0c;完全支持响应式编程&#xff0c;吞吐能力更强 Spring Cloud Gateway 是 Spring Cloud 的一个全新项目&#xff0c;用于构建基于 Spring 框架的 API 网关服务。它基于 Spring 5&a…

Python爬虫-批量爬取星巴克全国门店

前言 本文是该专栏的第22篇,后面会持续分享python爬虫干货知识,记得关注。 本文笔者以星巴克为例,通过Python实现批量爬取目标城市的门店数据以及全国的门店数据。 具体的详细思路以及代码实现逻辑,跟着笔者直接往下看正文详细内容。(附带完整代码) 正文 地址:aHR0cHM…

基于SpringBoot和Vue的课程作业管理系统的设计与实现

今天要和大家聊的是一款基于SpringBoot和Vue的课程作业管理系统的设计与实现。 &#xff01;&#xff01;&#xff01; 有需要的小伙伴可以通过文章末尾名片咨询我哦&#xff01;&#xff01;&#xff01; &#x1f495;&#x1f495;作者&#xff1a;李同学 &#x1f495;&am…

福昕阅读器 PDF 文档基本操作

福昕阅读器 PDF 文档基本操作 References 转至 PDF 顶部 快捷键&#xff1a;Home. 转至 PDF 顶部 快捷键&#xff1a;End. 打开超链接 文本选择工具 -> 手形工具 (Hand Tool) -> 点击超链接 福昕阅读器 同时在多个窗口中打开多个文件 文件 -> 偏好设置 -> 文…

开源博客项目Blog .NET Core源码学习(10:App.Framwork项目结构分析)

开源博客项目Blog的解决方案总共包括4个项目&#xff0c;其中App.Hosting项目包括所有的页面及控制器类&#xff0c;其它项目主要提供数据库访问、基础类型定义等。这四个项目的依赖关系如下图所示&#xff0c;本文主要分析App.Framwork项目的主要结构及主要文件的用途。   …

老胡的周刊(第134期)

老胡的信息周刊[1]&#xff0c;记录这周我看到的有价值的信息&#xff0c;主要针对计算机领域&#xff0c;内容主题极大程度被我个人喜好主导。这个项目核心目的在于记录让自己有印象的信息做一个留存以及共享。 &#x1f3af; 项目 Llama-Chinese[2] 专注于 Llama 模型在中文方…

nodejs+vue高校社团管理小程序的设计与实现python-flask-django-php

相比于以前的传统手工管理方式&#xff0c;智能化的管理方式可以大幅降低学校的运营人员成本&#xff0c;实现了高校社团管理的标准化、制度化、程序化的管理&#xff0c;有效地防止了高校社团管理的随意管理&#xff0c;提高了信息的处理速度和精确度&#xff0c;能够及时、准…

sentinel中StatisticSlot数据采集的原理

StatisticSlot数据采集的原理 时间窗口 固定窗口 在固定的时间窗口内&#xff0c;可以允许固定数量的请求进入&#xff1b;超过数量就拒绝或者排队&#xff0c;等下一个时间段进入, 如下图 时间窗长度划分为1秒 单个时间窗的请求阈值为3 上述存在一个问题, 假如9:18:04:…

8个常见的数据可视化错误以及如何避免它们

在当今以数据驱动为主导的世界里&#xff0c;清晰且具有洞察力的数据可视化至关重要。然而&#xff0c;在创建数据可视化时很容易犯错误&#xff0c;这可能导致对数据的错误解读。本文将探讨一些常见的糟糕数据可视化示例&#xff0c;并提供如何避免这些错误的建议。 本文总结了…

前端学习之用css和html做一个仿淘宝的导航栏

代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>仿淘宝界面案例</title><style>/* 最外层盒子 */.container{width: 270px;height: 385px;border: 1px solid rgb(255, 208, 0);bord…

服务消费微服务

文章目录 1.示意图2.环境搭建1.创建会员消费微服务模块2.删除不必要的两个文件3.检查父子模块的pom.xml文件1.子模块2.父模块 4.pom.xml 添加依赖&#xff08;刷新&#xff09;5.application.yml 配置监听端口和服务名6.com/sun/springcloud/MemberConsumerApplication.java 创…

LeetCode每日一题——移除链表元素

移除链表元素OJ链接&#xff1a;203. 移除链表元素 - 力扣&#xff08;LeetCode&#xff09; 题目&#xff1a; 思路&#xff1a; 这与之前的移除元素的题目很相似&#xff0c;那么我们同样可以用类似的做法&#xff08;双指针&#xff09;进行解题。但是这是一个链表删除&a…

代码随想录算法训练营Day56 ||leetCode 583. 两个字符串的删除操作 || 72. 编辑距离

647. 回文子串 dp[i][j]表示第i位开始&#xff0c;第j位结束的字符串是否为回文串 class Solution { public:int countSubstrings(string s) {vector<vector<bool>> dp(s.size(), vector<bool>(s.size(), false));int result 0;for (int i s.size() - 1…

大白话扩散模型(无公式版)

背景 传统的图像生成模型有GAN&#xff0c;VAE等&#xff0c;但是存在模式坍缩&#xff0c;即生成图片缺乏多样性&#xff0c;这是因为模型本身结构导致的。而扩散模型拥有训练稳定&#xff0c;保持图像多样性等特点&#xff0c;逐渐成为现在AIGC领域的主流。 扩散模型 正如…

笔记本和台式机主板内部结构分析

笔记本和态势机主板内存接口以及配件安装位置 笔记本主板 1 以thinkpad L-490为例,使用拆机小工具拆机&#xff0c;打开后面板&#xff0c;内部结构示意图如下 台式机主板 以技嘉-B660M-AORUS-PRO-AX型号主板为例 笔记本电脑和台式机电脑的相同之处 CPU&#xff1a;笔记本…

牛客题霸-SQL篇(刷题记录三)

本文基于前段时间学习总结的 MySQL 相关的查询语法&#xff0c;在牛客网找了相应的 MySQL 题目进行练习&#xff0c;以便加强对于 MySQL 查询语法的理解和应用。 由于涉及到的数据库表较多&#xff0c;因此本文不再展示&#xff0c;只提供 MySQL 代码与示例输出。 以下内容是…

字母大小写转换

#include <stdio.h>//字母大小写转换 int main() {char ch 0;while(scanf("%c",&ch) 1){if(ch > a && ch < z)printf("%c\n",ch-32);if(ch > A && ch < Z)printf("%c\n",ch32);getchar();//处理\n}retu…