Qt Remote Object(QtRO)动态Replica实现进程间通信

概述

前一篇文章我们介绍了QtRO静态Replica来实现进程间通信的基本用法,本文接着介绍QtRO的另一个部分,动态Replica,也就是Dynamic Replica。

QtRO本身是包含两种方式的,一个是静态,一个是动态,这两者实现方式稍有区别,其对应的场景也有所不同。

上一篇文章我们说到,要实现QtRO最重要的一步是创建rep文件,通过在rep文件中定义公共接口从而进行共享,而静态和动态Replica的最大的区别就在于,静态Replica比如在server和client端都需要一个相同的rep文件作为接口定义,而动态是指Client这边不再需要rep文件,而是运行时动态获取接口定义。

静态和动态的对比

既然支持这两种方式,那么该如何选择使用呢?接下来看看二者的优劣:

静态Replica:

优势

拥有明确的定义,更适合在C++中使用(因为有repc生成的头文件)。

支持POD等复杂结构的定义。

更高效。因为结构定义都已经在C++中定义好了,不需要动态传输、构建,节省了开销。

劣势

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

动态Replica:

优势

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

劣势

不支持POD等复杂结构定义。

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

POD类类型

上面提到POD类型,简单的介绍一下。

POD类类型就是指class、struct、union,且不具有用户定义的构造函数、析构函数、拷贝算子、赋值算子;不具有继承关系,因此没有基类;不具有虚函数,所以就没有虚表;非静态数据成员没有私有或保护属性的、没有引用类型的、没有非POD类类型的(即嵌套类都必须是POD)、没有指针到成员类型的(因为这个类型内含了this指针)

简单的理解就是,POD即float、int等基本C++类型,还有QString、QPoint等基本Qt类型。而静态Replica支持POD类型,动态Replica不支持POD类型。

示例

接下来我们在上一篇文章的代码基础上进行修改,改成动态Replica的模式。

Server端变化

要支持动态Replica,Server端不需要做太大的改变,只有两点需要注意:

上面我们提到动态Replica不支持POD类型,因为QPOD类型依赖于QDataStream的序列化和反序列化,这些都需要Qt元信息的支持。而动态Replica那边没有了rep文件,也就没有了对POD的反序列化能力,换句话说就是它不认识收到的POD二进制数据了。所以如果一定要使用POD类型的话,可以使用QVariantList和QVariantMap来定义。

Server 端enableRemoting时必须传入第二个参数name,因为没有了类型,动态Replica端不知道该链接那个Server端,所以只能通过name来区分

综上,需要修改的是:

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

Client端变化

动态Replica模式下,Client端不需要rep文件,所以在pro文件中,直接去掉:

#REPC_REPLICA +=

# ../Reps/CommonInterface.rep

然后在获得Replica的时候,需要用动态的版本:

m_pInterface = m_pRemoteNode->acquireDynamic("Interfaces1");//动态获取

这里的"Interfaces1"就是在Server端的名字。

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

只有当Replica发出initialized信号后,该Replica才有Source端的元信息(属性、信号与槽),才能被使用。

改动如下:

//只有Replica初始化好了才能真正使用它,要不然connect无效
connect(m_pInterface, &QRemoteObjectDynamicReplica::initialized, this, &MainWidget::onInitConnect);
...
void MainWidget::onInitConnect()
{
connect(m_pInterface,SIGNAL(sigMessage(QString)),
this,SLOT(onReceiveMsg(QString)));
connect(this,SIGNAL(sigSendMsg(QString)),
m_pInterface,SLOT(onMessage(QString)));
}

OK,改动已完成。

我们看一下Client端完整的代码:

#ifndef MAINWIDGET_H
#define MAINWIDGET_H
#include 
#include 
//#include "rep_CommonInterface_replica.h"
#include 
namespace Ui {
class MainWidget;
}
class MainWidget : public QWidget
{
Q_OBJECT
public:
explicit MainWidget(QWidget *parent = nullptr);
~MainWidget();
signals:
void sigSendMsg(QString msg);
private slots:
void onReceiveMsg(QString msg);
void on_pushButton_clicked();
void on_lineEdit_returnPressed();
void onInitConnect();
private:
void init();
private:
Ui::MainWidget *ui;
QRemoteObjectNode * m_pRemoteNode = nullptr;
QRemoteObjectDynamicReplica * m_pInterface = nullptr;
};
#endif // MAINWIDGET_H

mainwindow.cpp

#include "mainwidget.h"
#include "ui_mainwidget.h"
MainWidget::MainWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::MainWidget)
{
ui->setupUi(this);
this->setWindowTitle("This is Client");
init();
ui->textEdit->setReadOnly(true);
}
MainWidget::~MainWidget()
{
delete ui;
}
void MainWidget::init()
{
m_pRemoteNode = new QRemoteObjectNode(this);
m_pRemoteNode->connectToNode(QUrl("local:interfaces"));
// m_pInterface = m_pRemoteNode->acquire();
m_pInterface = m_pRemoteNode->acquireDynamic("Interfaces1");//动态获取
//只有Replica初始化好了才能真正使用它,要不然connect无效
connect(m_pInterface, &QRemoteObjectDynamicReplica::initialized, this, &MainWidget::onInitConnect);
}
/**
* @brief MainWidget::onReceiveMsg
* @param msg
* 接收服务器下发的消息
*/
void MainWidget::onReceiveMsg(QString msg)
{
ui->textEdit->append(QString("Server:") + msg);
}
void MainWidget::on_pushButton_clicked()
{
QString msg = ui->lineEdit->text();
if(!msg.isEmpty()){
// m_pInterface->onMessage(msg); //调用槽发送消息给服务器
emit sigSendMsg(msg);
}
ui->textEdit->append(QString("Client:") + msg);
ui->lineEdit->clear();
}
void MainWidget::on_lineEdit_returnPressed()
{
on_pushButton_clicked();
}
void MainWidget::onInitConnect()
{
connect(m_pInterface,SIGNAL(sigMessage(QString)),
this,SLOT(onReceiveMsg(QString)));
connect(this,SIGNAL(sigSendMsg(QString)),
m_pInterface,SLOT(onMessage(QString)));
}

OK,Server端只需要改动一行代码,所以就不列出来了。

注意,在静态Replica中,我们从Client发送消息给Server端时,直接调用rep文件中定义的槽onMessage就可以了,但是在动态Replica时不能直接调用了,会报错找不到这个槽,所以改成连接信号槽的方式,如下:

if(!msg.isEmpty()){
// m_pInterface->onMessage(msg); //调用槽发送消息给服务器
emit sigSendMsg(msg);
}
...
connect(this,SIGNAL(sigSendMsg(QString)),
m_pInterface,SLOT(onMessage(QString)));

点击领取Qt学习资料+视频教程~

链接:http://docs.qq.com/doc/DUlVwTW1FZlZuWE9G

展开阅读全文

页面更新:2024-06-18

标签:动态   算子   初始化   静态   函数   进程   信号   定义   类型   通信   方式   文件

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号

Top