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下发消息给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获取返回值。

  • 31
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: QT可以使用QProcess类来实现远程SSH文件的下载。下面是一个简单的示例代码: 首先,我们需要在.pro文件中添加libssh库的依赖。在.pro文件中添加以下行: ```cpp LIBS += -lssh ``` 接下来,我们创建一个新的类,例如RemoteSSHDownloader,继承自QObject类。在RemoteSSHDownloader.h文件中,我们定义以下成员变量和方法: ```cpp class RemoteSSHDownloader : public QObject { Q_OBJECT public: RemoteSSHDownloader(QObject *parent = nullptr); ~RemoteSSHDownloader(); public slots: void downloadFile(const QString &host, const QString &username, const QString &password, const QString &remoteFilePath, const QString &localFilePath); private slots: void processFinished(int exitCode, QProcess::ExitStatus exitStatus); private: QProcess *sshProcess; }; ``` 在RemoteSSHDownloader.cpp文件中,我们实现downloadFile方法和processFinished槽函数: ```cpp RemoteSSHDownloader::RemoteSSHDownloader(QObject *parent) : QObject(parent) { sshProcess = new QProcess(this); connect(sshProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(processFinished(int, QProcess::ExitStatus))); } RemoteSSHDownloader::~RemoteSSHDownloader() { delete sshProcess; } void RemoteSSHDownloader::downloadFile(const QString &host, const QString &username, const QString &password, const QString &remoteFilePath, const QString &localFilePath) { QStringList arguments; arguments << username + '@' + host << 'scp' << username + '@' + host + ':' + remoteFilePath << localFilePath; sshProcess->start("sshpass", arguments); } void RemoteSSHDownloader::processFinished(int exitCode, QProcess::ExitStatus exitStatus) { if (exitCode == 0 && exitStatus == QProcess::NormalExit) { qDebug() << "File downloaded successfully!"; } else { qDebug() << "File download failed!"; } } ``` 然后,我们可以在主要的Qt窗口或其他地方,实例化RemoteSSHDownloader类并调用downloadFile方法来下载远程SSH文件: ```cpp RemoteSSHDownloader downloader; downloader.downloadFile("example.com", "username", "password", "/path/to/remote/file.txt", "/path/to/local/file.txt"); ``` 请确保在调用downloadFile方法之前,已经建立了与远程SSH服务器的连接,并且已经安装了sshpass工具来处理自动化密码认证。 这样,通过使用QProcess和Libssh库,我们就可以在Qt实现远程SSH文件的下载了。有了这个基础,你还可以根据需要进一步扩展功能,如添加进度显示、错误处理等。 ### 回答2: Qt是一个跨平台的C++应用程序开发框架,可以用于开发各种类型的应用程序,包括网络应用程序。要实现远程SSH文件的下载,我们可以利用Qt的网络编程库和SSH客户端库。 首先,我们需要使用Qt的网络编程库建立与SSH服务器的连接。通过创建一个QTcpSocket对象,并通过connectToHost函数连接到SSH服务器的IP地址和端口号。然后,我们可以使用write函数发送SSH握手协议,以建立SSH连接。 接下来,我们需要使用SSH客户端库执行远程命令来下载文件。可以使用exec函数执行远程命令,例如"scp user@remote:source_file local_file"来下载文件。在这个命令中,user@remote是SSH服务器的用户名和地址,source_file是要下载的文件路径,local_file是本地保存的文件路径。执行命令后,可以使用waitForFinished函数等待命令执行完成。 最后,我们可以使用Qt的文件操作函数,例如QFile和QIODevice,来读取下载的文件内容,并将其保存到本地文件中。 总结起来,使用Qt实现远程SSH文件的下载需要借助Qt的网络编程库建立SSH连接,使用SSH客户端库执行远程命令来下载文件,然后使用Qt的文件操作函数保存下载的文件内容。通过这样的步骤,我们可以实现通过SSH进行远程文件下载的功能。 ### 回答3: Qt作为跨平台的应用开发框架,提供了丰富的网络编程工具和库,能够轻松实现远程SSH文件的下载功能。下面是使用Qt实现远程SSH文件下载的示例代码: 首先,在项目中引入Qt网络库: ```cpp #include <QtNetwork> ``` 然后,在需要进行文件下载的函数中,使用QProcess类执行远程Shell命令,使用SCP命令实现SSH文件下载: ```cpp void downloadFile() { // 创建 QProcess 对象 QProcess process; // 设置远程SSH服务器的信息 QString sshHost = "your_ssh_host"; QString sshUser = "your_ssh_user"; QString sshPassword = "your_ssh_password"; QString remoteFilePath = "remote_file_path"; QString localFilePath = "local_file_path"; // 构建SCP下载命令 QString command = QString("scp %1@%2:%3 %4") .arg(sshUser) .arg(sshHost) .arg(remoteFilePath) .arg(localFilePath); // 启动QProcess并执行命令 process.start(command); // 等待命令执行完毕 process.waitForFinished(); // 判断命令执行结果 if (process.exitCode() == 0) { qDebug() << "文件下载成功"; } else { qDebug() << "文件下载失败:" << process.readAllStandardError(); } } ``` 通过以上代码,我们可以实现Qt中使用SCP命令进行远程SSH文件的下载。需要注意的是,为了确保SCP命令的正常执行,需要在运行程序的环境中安装和配置OpenSSH。另外,为了保证下载过程的安全性,建议将敏感的SSH登录信息存储在安全的地方,并在运行时使用相应的方法进行获取。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值