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 <QWidget> #include <QRemoteObjectNode> //#include "rep_CommonInterface_replica.h" #include <QRemoteObjectDynamicReplica>namespace Ui { class MainWidget; }class MainWidget : public QWidget {Q_OBJECTpublic: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_Hmainwindow.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)));以上,運行結果和靜態Replica一樣,詳見上一篇文章介紹。
上述代碼在這里
參考文章:
https://doc.qt.io/qt-5/qtremoteobjects-index.html
https://zhuanlan.zhihu.com/p/37108172
總結
以上是生活随笔為你收集整理的Qt Remote Object(QtRO)动态Replica实现进程间通信的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Qt Remote Object(QtR
- 下一篇: git 常用别名设置