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

news2024/11/18 9:35:02
简介

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下发消息给client

    SLOT(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.h



class CommonInterface : public CommonInterfaceSimpleSource

{

    Q_OBJECT

public:

    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_NAMESPACE

namespace Ui { class Dialog; }

QT_END_NAMESPACE



class Dialog : public QDialog

{

    Q_OBJECT



public:

    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_NAMESPACE

namespace Ui { class Dialog; }

QT_END_NAMESPACE



class Dialog : public QDialog

{

    Q_OBJECT

public:

    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.coloradmin.cn/o/1541624.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

科技云报道:造完“大模型”,“具身智能”将引领AI下一个浪潮?

科技云报道原创。 资深机器人专家Eric Jang不久前曾预言&#xff1a;“ChatGPT 曾在一夜之间出现。我认为&#xff0c;有智慧的机器人技术也将如此。” 3月13日深夜&#xff0c;一段人形机器人的视频开始热传。 在视频中&#xff0c;Figure的人形机器人&#xff0c;可以完全…

代码随想录阅读笔记-栈与队列【有效的括号】

题目 给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串&#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。注意空字符串可被认为是有效字符串。 …

python必刷算法

数组 二分法 二分法满足从小到大排序无重复元素 1 两个边界&#xff0c;left,right 2 中间值的选择 3 边界问题考虑有两种 left < right 当left < right的时候&#xff0c;说明mid比较的时候已经比较了left right def search(self, nums: List[int], target: int) ->…

数据分析与挖掘

数据起源&#xff1a; 规模庞大&#xff0c;结构复杂&#xff0c;难以通过现有商业工具和技术在可容忍的时间内获取、管理和处理的数据集。具有5V特性&#xff1a;数量&#xff08;Volume&#xff09;&#xff1a;数据量大、多样性&#xff08;Variety&#xff09;&#xff1a…

基于VS code 实现Java前后端打通—基础—使用Springboot+postgreSql+mybatis+Navicat

前言&#xff1a; 作者学习webjava后的而总结&#xff0c;总的流程概括就是先使用springboot创建项目&#xff0c;在application.properties中完成相应的postgreSql和mybaits的环境配置和.xml文件中dependecy依赖配置&#xff0c;entities实现数据表的类型模板&#xff0c;分别…

隐私计算实训营学习四:SecretFlow的安装和部署

文章目录 一、SecretFlow安装二、SecretFolw部署模式简介三、SecretFlow部署-仿真模式四、SecretFlow部署-生产模式 一、SecretFlow安装 SecretFlow运行要求&#xff1a; Python > 3.8操作系统&#xff1a;CentOS7、Anolis8、Ubuntu 18.04/20.04、macOS 11.1、WSL2资源&am…

前端框架前置课(1)---AJAX阶段

1. AJAX入门 1.1 AJAX概念和axios使用 1.1.1 什么是AJAX? 1.1.2 怎么用AJAX? 引入axios.js 获取省份列表数据 1.2 认识URL 1.3 URL查询参数 1.4 常用请求方和数据提交 1.5 HTTP协议-报文 1.5.1 HTTP响应状态码 1.5.1.1 状态码&#xff1a;1XX&#xff08;信息&#xff09…

论文阅读:UniFormer和UniFormerV2

文章目录 UNIFormer动机方法动态位置嵌入(DPE)多头关系聚合器(MHRA) 模型代码总结 UniFormerV2动机方法整体框架实现细节 总结 UNIFormer 本文主要介绍了UniFormer: Unified Transformer for Efficient Spatial-Temporal Representation Learning 代码&#xff1a;https://git…

购买腾讯云服务器需要多少钱?价格表查询

腾讯云服务器多少钱一年&#xff1f;61元一年起。2024年最新腾讯云服务器优惠价格表&#xff0c;腾讯云轻量2核2G3M服务器61元一年、2核2G4M服务器99元一年可买三年、2核4G5M服务器165元一年、3年756元、轻量4核8M12M服务器646元15个月、4核16G10M配置32元1个月、312元一年、8核…

Python综合实战案例-数据清洗分析

写在前面&#xff1a; 本次是根据前文讲解的爬虫、数据清洗、分析进行的一个纵隔讲解案例&#xff0c;也是对自己这段时间python爬虫、数据分析方向的一个总结。 本例设计一个豆瓣读书数据⽂件&#xff0c;book.xlsx⽂件保存的是爬取豆瓣⽹站得到的图书数据&#xff0c;共 6067…

html网页制作-3D旋转卡片

网页制作-3D旋转卡片 两种旋转卡片的制作 旋转卡片&#xff08;1&#xff09; 代码 html代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-wid…

Transformer的前世今生 day03(Word2Vec、如何使用在下游任务中)

前情回顾 由上一节&#xff0c;我们可以得到&#xff1a; 任何一个独热编码的词都可以通过Q矩阵得到一个词向量&#xff0c;而词向量有两个优点&#xff1a; 可以改变输入的维度&#xff08;原来是很大的独热编码&#xff0c;但是我们经过一个Q矩阵后&#xff0c;维度就可以控…

linux命令(八)

搜索 其实很多人使用linux的是因为服务器是linux系统&#xff0c;既然是服务器&#xff0c;那查找日志肯定是大家用的很多的了&#xff0c;这一节就来介绍一下搜索的命令 grep 先看一下我的文件中的内容是什么 查找不包含该字符串的行 -v v代表的invert-match(不匹配的行) …

基于Java中的SSM框架实现电能计量与客户服务管理系统项目【项目源码+论文说明】计算机毕业设计

基于Java中的SSM框架实现电能计量与客户服务管理系统演示 摘要 当前时代的两个突出特征是世界经济一体化和以计算机为代表的信息技术的迅速发展。为了使组织在激烈的竞争中保持实力和发展&#xff0c;它必须对迅速变化的环境做出有效而有效的响应。 管理信息系统的应用可以提供…

202446读书笔记|《夜风颂》——生命的内核是过往和希望 有情在朝暮 长聚长相思

202446读书笔记|《夜风颂》——生命的内核是过往和希望 有情在朝暮 长聚长相思 序现代诗古体诗 《夜风颂》作者王锴&#xff0c;前段时间加入书架的书&#xff0c;前边有几首现代诗挺惊艳&#xff0c;蛮喜欢的&#xff0c;后边古体诗稍逊色些。值得一读的一本小诗集。 序 海鸥之…

11.创建后台系统项目

后台系统项目 兼容性 vite官网&#xff1a;https://vitejs.dev/ vite中文网&#xff1a;https://cn.vitejs.dev/ vite需要node.js版本 >14.0.0&#xff0c;建议16 node -v 查看版本号 创建项目 进入存放目录 执行命令 npm create vitelatest 选择vue框架 选择typescript…

缓存穿透、缓存击穿、缓存雪崩及其解决方法

缓存穿透、缓存击穿、缓存雪崩是redis的三大问题。 在介绍这三大问题之前&#xff0c;我们需要先了解Redis作为一个缓存中间件&#xff0c;在项目中是如何工作的。首先看一下在没有缓存中间件的时候的系统数据访问的架构图&#xff1a; 客户端发起一个查询请求的时候&#xff…

一个软开关,长按开机,自动关机的实现。

连接说明&#xff1a; 1.ADP_PWR接适配器输入插座 当适配器插入的时候 ADP_PWR接入适配器7.4~8.4V电压 2.ON/OFF_KEY 接开关按键&#xff0c;当开关按下的时候&#xff0c;ON/OFF_KEY 接入电池电压 7.4V 3.ON/OFF_CTRL接单片机IO口 开机实现说明&#xff1a; 1.长按开…

存储随笔原创科普视频首播~

一周之前&#xff0c;存储随笔创建了B站账号。小编利用上个周末休息时间专门研究了B站视频录制的各种方案。发现并没有想象的很容易&#xff0c;先花了很长时间准备了一个PPT&#xff0c;再准备演讲大纲&#xff0c;最终磕磕绊绊完成了首期原创视频录制&#xff01; 可能不尽如…

Mybatis中显示插入数据成功,但在数据库中却没有显示插入的数据

1、在mybatis-config.xml中查看是否添加了JDBC&#xff0c;并引入了映射文件 2、在测试文件中&#xff0c;结尾是否添加提交事务&#xff1a;sqlSession.commit() 添加了这一步就能够将数据提交到数据库中&#xff0c;最后再关闭事务&#xff1a;sqlSession.close() * 如果运…