赞
踩
前一篇文章我们介绍了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类类型就是指class、struct、union,且不具有用户定义的构造函数、析构函数、拷贝算子、赋值算子;不具有继承关系,因此没有基类;不具有虚函数,所以就没有虚表;非静态数据成员没有私有或保护属性的、没有引用类型的、没有非POD类类型的(即嵌套类都必须是POD)、没有指针到成员类型的(因为这个类型内含了this指针)
简单的理解就是,POD即float、int等基本C++类型,还有QString、QPoint等基本Qt类型。而静态Replica支持POD类型,动态Replica不支持POD类型。
接下来我们在上一篇文章的代码基础上进行修改,改成动态Replica的模式。
要支持动态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"));
动态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 <QWidget> #include <QRemoteObjectNode> //#include "rep_CommonInterface_replica.h" #include <QRemoteObjectDynamicReplica> 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<CommonInterfaceReplica>(); 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)));
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。