当前位置:   article > 正文

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

qtro 博客

概述

前一篇文章我们介绍了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端的元信息(属性、信号与槽),才能被使用。

改动如下:

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

OK,改动已完成。

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

  1. #ifndef MAINWIDGET_H
  2. #define MAINWIDGET_H
  3. #include <QWidget>
  4. #include <QRemoteObjectNode>
  5. //#include "rep_CommonInterface_replica.h"
  6. #include <QRemoteObjectDynamicReplica>
  7. namespace Ui {
  8. class MainWidget;
  9. }
  10. class MainWidget : public QWidget
  11. {
  12. Q_OBJECT
  13. public:
  14. explicit MainWidget(QWidget *parent = nullptr);
  15. ~MainWidget();
  16. signals:
  17. void sigSendMsg(QString msg);
  18. private slots:
  19. void onReceiveMsg(QString msg);
  20. void on_pushButton_clicked();
  21. void on_lineEdit_returnPressed();
  22. void onInitConnect();
  23. private:
  24. void init();
  25. private:
  26. Ui::MainWidget *ui;
  27. QRemoteObjectNode * m_pRemoteNode = nullptr;
  28. QRemoteObjectDynamicReplica * m_pInterface = nullptr;
  29. };
  30. #endif // MAINWIDGET_H

mainwindow.cpp

  1. #include "mainwidget.h"
  2. #include "ui_mainwidget.h"
  3. MainWidget::MainWidget(QWidget *parent) :
  4. QWidget(parent),
  5. ui(new Ui::MainWidget)
  6. {
  7. ui->setupUi(this);
  8. this->setWindowTitle("This is Client");
  9. init();
  10. ui->textEdit->setReadOnly(true);
  11. }
  12. MainWidget::~MainWidget()
  13. {
  14. delete ui;
  15. }
  16. void MainWidget::init()
  17. {
  18. m_pRemoteNode = new QRemoteObjectNode(this);
  19. m_pRemoteNode->connectToNode(QUrl("local:interfaces"));
  20. // m_pInterface = m_pRemoteNode->acquire<CommonInterfaceReplica>();
  21. m_pInterface = m_pRemoteNode->acquireDynamic("Interfaces1");//动态获取
  22. //只有Replica初始化好了才能真正使用它,要不然connect无效
  23. connect(m_pInterface, &QRemoteObjectDynamicReplica::initialized, this, &MainWidget::onInitConnect);
  24. }
  25. /**
  26. * @brief MainWidget::onReceiveMsg
  27. * @param msg
  28. * 接收服务器下发的消息
  29. */
  30. void MainWidget::onReceiveMsg(QString msg)
  31. {
  32. ui->textEdit->append(QString("Server:") + msg);
  33. }
  34. void MainWidget::on_pushButton_clicked()
  35. {
  36. QString msg = ui->lineEdit->text();
  37. if(!msg.isEmpty()){
  38. // m_pInterface->onMessage(msg); //调用槽发送消息给服务器
  39. emit sigSendMsg(msg);
  40. }
  41. ui->textEdit->append(QString("Client:") + msg);
  42. ui->lineEdit->clear();
  43. }
  44. void MainWidget::on_lineEdit_returnPressed()
  45. {
  46. on_pushButton_clicked();
  47. }
  48. void MainWidget::onInitConnect()
  49. {
  50. connect(m_pInterface,SIGNAL(sigMessage(QString)),
  51. this,SLOT(onReceiveMsg(QString)));
  52. connect(this,SIGNAL(sigSendMsg(QString)),
  53. m_pInterface,SLOT(onMessage(QString)));
  54. }

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

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

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

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

闽ICP备14008679号