当前位置:   article > 正文

Qt Remote Object实现进程间通信_qremoteobject

qremoteobject

1 概述

Qt Remote Object简称QtRO,这是Qt5.9以后官方推出来的新模块,专门用于进程间通信(IPC)。QtRO本质上是一个点对点的通信网络。每个进程通过QRemoteObjectNode接入QtRO网络。功能提供节点(可以理解为服务器)需要使用QRemoteObjectHost将一个提供实际功能的QObject派生类注册进QtRO网络中,然后其他使用该功能的程序则通过各自的QRemoteObjectNode连接到该Host上,然后acquire一个该功能对象的Replica。等到该Replica初始化好后,该程序就能够使用Replica中的信号、槽以及属性,就好像功能类就在本地一样。QTRO分为两种Replica,一种时动态的Replica,一种是静态的Replica。

2静态和动态的对比

静态Replica:

优势

拥有明确的定义,更适合在C++中使用(因为有repc生成的头文件)。
支持POD等复杂结构的定义。
更高效。因为结构定义都已经在C++中定义好了,不需要动态传输、构建,节省了开销。

劣势

Source端和Replica端必须严格使用同一版本的rep文件,即使rep文件内只是添加了一行注释,否则会连接不上。

动态Replica

优势

由于Client端不需要rep文件,所以Server端可以随时修改,这就避免了静态模式下的缺点。

劣势

不支持POD等复杂结构定义。
必须等初始化后才能使用,给编程增加了额外复杂度,同时增加了构建连接的额外开销。这个是动态这个特性决定的。

pod类型简单的理解就是float、int、string、class、struct等类型。

3 静态Replica

服务端

1创建rep文件

rep文件是一种DSL(Domain Specific Language),专门用于定义QtRO接口。在编译的时候,该文件会首先经过repc.exe这个程序处理,生成对应的头文件和源文件。
commoninterface.rep

class CommonInterface
{
    SIGNAL(sigMessage(QString msg))   //server下发消息给client
    SLOT(void onMessage(QString msg)) //server接收client的消息
}
  • 1
  • 2
  • 3
  • 4
  • 5

2编译

添加QtRO模块

QT += remoteobjects
  • 1

添加rep文件

REPC_SOURCE += \
    ./Reps/CommonInterface.rep
  • 1
  • 2

编译,然后在程序的输出目录可以找到生成的rep头文件
在这里插入图片描述

3实现功能类

创建一个类,继承于自动生成的这个类,并实现其中所有的虚函数。
CommonInterface.h

#ifndef COMMONINTERFACE_H
#define COMMONINTERFACE_H

#include "rep_CommonInterface_source.h"

class CommonInterface:public CommonInterfaceSource
{
    Q_OBJECT
public:
    explicit CommonInterface(QObject * parent = nullptr);

    //接收消息
    void onMessage(QString msg) override;
    //发送消息
    void sendMsg(QString msg);
signals:
    void sigReceiveMsg(QString);
};

#endif // COMMONINTERFACE_H

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

CommonInterface.cpp

#include "CommonInterface.h"

CommonInterface::CommonInterface(QObject *parent):
    CommonInterfaceSource(parent)
{

}

void CommonInterface::onMessage(QString msg)
{
    emit sigReceiveMsg(msg);
}

void CommonInterface::sendMsg(QString msg)
{
    sigMessage(msg);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

4实现Server的主逻辑

Widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include "CommonInterface.h"
#include <QRemoteObjectHost>
#include <QDateTime>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
private:
    void init();
private slots:
    void onReceiveMsg(QString msg);
    void sendBtnClicked();
    void lineRetrunPressed();
private:
    Ui::Widget *ui;
    CommonInterface* m_pInterface=nullptr;
    QRemoteObjectHost* m_pHost=nullptr;
};
#endif // WIDGET_H
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

Widget.cpp

#include "Widget.h"
#include "ui_Widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    init();
}

Widget::~Widget()
{
    delete ui;
}

void Widget::init()
{
    setWindowTitle("This is Server");
    ui->inputLine->setFocus();

    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,&Widget::onReceiveMsg);

    connect(ui->sendBtn,&QPushButton::clicked,this,&Widget::sendBtnClicked);
    connect(ui->inputLine,&QLineEdit::returnPressed,this,&Widget::lineRetrunPressed);
}

void Widget::onReceiveMsg(QString msg)
{
    QDateTime curDateTime=QDateTime::currentDateTime();
    QString temp=curDateTime.toString("yyyy-MM-dd hh:mm:ss");

    ui->textEdit->append(temp+QString(" Client: ") + msg);
}

void Widget::sendBtnClicked()
{
    QString msg = ui->inputLine->text();
    if(!msg.isEmpty()){
        m_pInterface->sendMsg(msg);
    }
    QDateTime curDateTime=QDateTime::currentDateTime();
    QString temp=curDateTime.toString("yyyy-MM-dd hh:mm:ss");

    ui->textEdit->append(temp+QString(" Server: ") + msg);
    ui->inputLine->clear();
}

void Widget::lineRetrunPressed()
{
    sendBtnClicked();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

注意

m_pHost->setHostUrl(QUrl("local:interfaces"));
  • 1

字符串格式为"local:xxxx",其中xxxx必须是唯一的字符串,不同和其他程序有冲突,否则将会无法连接。
服务端到这里就OK了。

客户端

1创建rep文件夹

客户端共用一个rep文件,然后配置por文件

QT += remoteobjects

REPC_REPLICA += \
    ./Rep/CommonInterface.rep
  • 1
  • 2
  • 3
  • 4

注意,这里和Server端添加方式不一样,server端是REPC_SOURCE。

2编译

找到对应生成的rep头文件
在这里插入图片描述
和server端不同的是,client端不需要重新实现功能类,只需要在主窗口中连接server端即可。

3实现Client的主逻辑

Widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QRemoteObjectNode>
#include "rep_CommonInterface_replica.h"
#include <QDateTime>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
private:
    void init();
private slots:
    void onReceiveMsg(QString msg);
    void sendBtnClicked();
    void lineRetrunPressed();

private:
    Ui::Widget *ui;
    QRemoteObjectNode * m_pRemoteNode = nullptr;
    CommonInterfaceReplica * m_pInterface = nullptr;
};
#endif // WIDGET_H
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

Widget.cpp

#include "Widget.h"
#include "ui_Widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    init();
}

Widget::~Widget()
{
    delete ui;
}
void Widget::init()
{
    setWindowTitle("This is Client");
    ui->inputLine->setFocus();

    m_pRemoteNode = new QRemoteObjectNode(this);
    m_pRemoteNode->connectToNode(QUrl("local:interfaces"));
    m_pInterface = m_pRemoteNode->acquire<CommonInterfaceReplica>();

    connect(m_pInterface,&CommonInterfaceReplica::sigMessage,
            this,&Widget::onReceiveMsg);

    connect(ui->sendBtn,&QPushButton::clicked,this,&Widget::sendBtnClicked);
    connect(ui->inputLine,&QLineEdit::returnPressed,this,&Widget::lineRetrunPressed);
}

void Widget::onReceiveMsg(QString msg)
{
    QDateTime curDateTime=QDateTime::currentDateTime();
    QString temp=curDateTime.toString("yyyy-MM-dd hh:mm:ss");

    ui->textEdit->append(temp+QString(" Server: ") + msg);
}

void Widget::sendBtnClicked()
{
    QString msg = ui->inputLine->text();
    if(!msg.isEmpty()){
        m_pInterface->onMessage(msg); //调用槽发送消息给服务器
    }
    QDateTime curDateTime=QDateTime::currentDateTime();
    QString temp=curDateTime.toString("yyyy-MM-dd hh:mm:ss");

    ui->textEdit->append(temp+QString(" Client: ") + msg);
    ui->inputLine->clear();
}

void Widget::lineRetrunPressed()
{
    sendBtnClicked();
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58

注意

m_pRemoteNode->connectToNode(QUrl("local:interfaces"));
m_pInterface = m_pRemoteNode->acquire<CommonInterfaceReplica>();
  • 1
  • 2

这里的连接地址和server的必须保持一致,然后通过acquire(); 连接Server端。
客户端到这里就OK了。

运行截图

在这里插入图片描述

3 动态Replica

将上面的示例改成动态Replica。

Server端变化

m_pHost->enableRemoting(m_pInterface,QStringLiteral("Interfaces1"));
  • 1

必须传入第二个参数name。

Client端变化

不需要rep文件,pro文件中直接去掉

#REPC_REPLICA += \
#    ./Rep/CommonInterface.rep
  • 1
  • 2

然后再获取Replica的时候,需要用动态的版本

QRemoteObjectDynamicReplica  * m_pInterface = nullptr;
  • 1
m_pInterface = m_pRemoteNode->acquireDynamic("Interfaces1");//动态获取
  • 1

此外,还有一个关键的点需要改动,因为没了rep文件,程序刚启动时不知道连接的Server端长啥样。所以动态Replica的内部原理是建立连接后,首先获得Source端的元信息,然后动态地在Replica端构建属性、信号和槽。这些构造完毕后,Replica会发送一个initialized的信号,这之后该Replica才能够真正被使用。
只有当Replica发出initialized信号后,该Replica才有Source端的元信息(属性、信号与槽),才能被使用。

 	setWindowTitle("This is Client");
    ui->inputLine->setFocus();

    m_pRemoteNode = new QRemoteObjectNode(this);
    m_pRemoteNode->connectToNode(QUrl("local:interfaces"));
    m_pInterface=m_pRemoteNode->acquireDynamic("Interfaces1");//动态获取

    //只有Replica初始化好了才能真正使用它,要不然connect无效
    connect(m_pInterface, &QRemoteObjectDynamicReplica::initialized, this, &Widget::onInitConnect);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

到这就全部改完了

Client端完整代码

Widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QRemoteObjectNode>
#include "rep_CommonInterface_replica.h"
#include <QDateTime>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
private:
    void init();
private slots:
    void onReceiveMsg(QString msg);
    void sendBtnClicked();
    void lineRetrunPressed();
    void onInitConnect();
signals:
    void sigSendMsg(QString);
private:
    Ui::Widget *ui;
    QRemoteObjectNode * m_pRemoteNode = nullptr;
    QRemoteObjectDynamicReplica  * m_pInterface = nullptr;
};
#endif // WIDGET_H

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

Widget.cpp

#include "Widget.h"
#include "ui_Widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    init();
}

Widget::~Widget()
{
    delete ui;
}
void Widget::init()
{
    setWindowTitle("This is Client");
    ui->inputLine->setFocus();

    m_pRemoteNode = new QRemoteObjectNode(this);
    m_pRemoteNode->connectToNode(QUrl("local:interfaces"));
    m_pInterface=m_pRemoteNode->acquireDynamic("Interfaces1");//动态获取

    //只有Replica初始化好了才能真正使用它,要不然connect无效
    connect(m_pInterface, &QRemoteObjectDynamicReplica::initialized, this, &Widget::onInitConnect);
}

void Widget::onReceiveMsg(QString msg)
{
    QDateTime curDateTime=QDateTime::currentDateTime();
    QString temp=curDateTime.toString("yyyy-MM-dd hh:mm:ss");

    ui->textEdit->append(temp+QString(" Server: ") + msg);
}

void Widget::sendBtnClicked()
{
    QString msg = ui->inputLine->text();
    if(!msg.isEmpty()){
        emit sigSendMsg(msg);
    }
    QDateTime curDateTime=QDateTime::currentDateTime();
    QString temp=curDateTime.toString("yyyy-MM-dd hh:mm:ss");

    ui->textEdit->append(temp+QString(" Client: ") + msg);
    ui->inputLine->clear();
}

void Widget::lineRetrunPressed()
{
    sendBtnClicked();
}

void Widget::onInitConnect()
{
    connect(m_pInterface,SIGNAL(sigMessage(QString)),this,SLOT(onReceiveMsg(QString)));
    connect(this,SIGNAL(sigSendMsg(QString)),m_pInterface,SLOT(onMessage(QString)));

    connect(ui->sendBtn,&QPushButton::clicked,this,&Widget::sendBtnClicked);
    connect(ui->inputLine,&QLineEdit::returnPressed,this,&Widget::lineRetrunPressed);
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64

运行截图

在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/184754
推荐阅读
相关标签
  

闽ICP备14008679号