赞
踩
Qt Remote Object简称QtRO,这是Qt5.9以后官方推出来的新模块,专门用于进程间通信(IPC)。QtRO本质上是一个点对点的通信网络。每个进程通过QRemoteObjectNode接入QtRO网络。功能提供节点(可以理解为服务器)需要使用QRemoteObjectHost将一个提供实际功能的QObject派生类注册进QtRO网络中,然后其他使用该功能的程序则通过各自的QRemoteObjectNode连接到该Host上,然后acquire一个该功能对象的Replica。等到该Replica初始化好后,该程序就能够使用Replica中的信号、槽以及属性,就好像功能类就在本地一样。QTRO分为两种Replica,一种时动态的Replica,一种是静态的Replica。
拥有明确的定义,更适合在C++中使用(因为有repc生成的头文件)。
支持POD等复杂结构的定义。
更高效。因为结构定义都已经在C++中定义好了,不需要动态传输、构建,节省了开销。
Source端和Replica端必须严格使用同一版本的rep文件,即使rep文件内只是添加了一行注释,否则会连接不上。
由于Client端不需要rep文件,所以Server端可以随时修改,这就避免了静态模式下的缺点。
不支持POD等复杂结构定义。
必须等初始化后才能使用,给编程增加了额外复杂度,同时增加了构建连接的额外开销。这个是动态这个特性决定的。
pod类型简单的理解就是float、int、string、class、struct等类型。
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的消息
}
添加QtRO模块
QT += remoteobjects
添加rep文件
REPC_SOURCE += \
./Reps/CommonInterface.rep
编译,然后在程序的输出目录可以找到生成的rep头文件
创建一个类,继承于自动生成的这个类,并实现其中所有的虚函数。
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
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); }
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
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(); }
注意
m_pHost->setHostUrl(QUrl("local:interfaces"));
字符串格式为"local:xxxx",其中xxxx必须是唯一的字符串,不同和其他程序有冲突,否则将会无法连接。
服务端到这里就OK了。
客户端共用一个rep文件,然后配置por文件
QT += remoteobjects
REPC_REPLICA += \
./Rep/CommonInterface.rep
注意,这里和Server端添加方式不一样,server端是REPC_SOURCE。
找到对应生成的rep头文件
和server端不同的是,client端不需要重新实现功能类,只需要在主窗口中连接server端即可。
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
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(); }
注意
m_pRemoteNode->connectToNode(QUrl("local:interfaces"));
m_pInterface = m_pRemoteNode->acquire<CommonInterfaceReplica>();
这里的连接地址和server的必须保持一致,然后通过acquire(); 连接Server端。
客户端到这里就OK了。
将上面的示例改成动态Replica。
m_pHost->enableRemoting(m_pInterface,QStringLiteral("Interfaces1"));
必须传入第二个参数name。
不需要rep文件,pro文件中直接去掉
#REPC_REPLICA += \
# ./Rep/CommonInterface.rep
然后再获取Replica的时候,需要用动态的版本
QRemoteObjectDynamicReplica * m_pInterface = nullptr;
m_pInterface = m_pRemoteNode->acquireDynamic("Interfaces1");//动态获取
此外,还有一个关键的点需要改动,因为没了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);
到这就全部改完了
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
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); }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。