Qt Remote Objects 是一个基于进程通信的机制,让开发人员调用一个其他进程的方法就像本地调用一样。这里我写一个简单的样例,主要是能够在进程A写入数据,但实际上是进程B写入文件数据。这样子的方式,让开发者写代码就像没有网络或者管道一样。
cmake添加字段
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets RemoteObjects)
此处引用了qt的对应模块
添加Qt指定的接口模板
file(GLOB REPSOURCE APPEND "*.rep" )
#用于建立服务端,自动编译生成节点功能模板
qt6_add_repc_sources(SourceNode
${REPSOURCE}
)
#用于客户端,实际使用者,自动生成节点代理
qt6_add_repc_replicas(SourceNode
${REPSOURCE}
)
上面两个也可以合成一段cmake,qt文档里面有对应的cmake方法。
链接对应的库
target_link_libraries(SourceNode PRIVATE
Qt${QT_VERSION_MAJOR}::Widgets
Qt6::RemoteObjects
)
Node01.rep
class Node01
{
PROP(bool actived=false);
SLOT(server_slot(bool state));
SLOT(writeFile(const QString&,const QByteArray&));
SIGNAL(message(const QString&));
}
上述actived只是为了测试信号是否有效。
server_slot只是为了测试槽函数是否正确链接
writeFile 是真正的跨进程写文件
message 只是为了方便的显示日志
Node01Impl.cpp
文件是作为实际提供写功能的服务端代码。
按照qt的rep接口要求,自动生成的rep_Node01_source.h定义了需要实现的接口(纯虚函数)
writeFile 需要作为槽函数实现写文件功能
void Node01Impl::writeFile(const QString &fileName, const QByteArray &__repc_variable_2)
{
auto fileP = mFiles.value(fileName);
if(fileP==nullptr)
{
fileP.reset(new QFile(fileName));
mFiles.insert(fileName,fileP);
}
if(!fileP->isOpen())
{
auto ret = fileP->open(QIODevice::WriteOnly);
if(!ret)
{
return ;
}
}
if(__repc_variable_2.isEmpty())
{
fileP->close();
mFiles.remove(fileName);
return ;
}
fileP->write(__repc_variable_2);
qDebug()<<" write "<<fileName <<" msg "<<__repc_variable_2;
return ;
}
上述代码效率不高,仅仅是为了实现测试效果,实际使用应该是单例文件管理,不去判断过多。
代码简单的实现了创建文件,在发送过来的信息是空的时候,关闭并且结束文件控制。
实际使用:
进程A,创建服务,可以理解为建立一个tcpserver,发布一个节点,提供大家功能
void MainWindow::on_pushButton_2_clicked()
{
//注册一个服务集群,提供路由管理调用
if(registry==nullptr)
{
registry= new QRemoteObjectRegistryHost(registerUrl,this);
registry->setName("vvvv");
}
//创建服务,并且注册到指定的集群
if(mHost==nullptr)
{
mHost = new QRemoteObjectHost(hostUrl,registerUrl);
mHost->setName("zzc");
}
//创建我的服务
if(mNode1==nullptr)
{
mNode1 = new Node01Impl(this);
}
mHost->enableRemoting(mNode1,"node1222");
registry->enableRemoting(mNode1,"node001");
}
上述代码,建立了一个服务,把之前的rep中实现的功能对象注册,也就是,如果其他进程找到了我注册对象,就可以使用它的函数。
进程B,查找服务,可以理解为tcp尝试去和服务器连接,找到之前注册的服务。并且创建他们之间的代理对象(不需要我们关心是断开了)
//connect 创建一个用户节点,准备调用远端的方法
if(repNode==nullptr)
{
//连结集群服务
repNode = new QRemoteObjectNode(registerUrl, this);
//repNode = new QRemoteObjectNode( this);
//连结指定地址服务,上面构造如果注册节点有效,此处可以不需要
repNode->connectToNode(hostUrl);
repNode->setName("clientNode");
//此处如果不等待,下面立即获取实力可能是会失败的
repNode->waitForRegistry();
}
QStringList ins;
ins<<" instaces :"<<repNode->instances<Node01Replica>();
ui->textBrowser->append(ins.join(" "));
if(nodePtr==nullptr)
{
//获取服务,使用服务,node001,node1222获取到的都是同一个对象,只是注册的名称不一样
nodePtr.reset(repNode->acquire<Node01Replica>("node001"));
auto dyn = repNode->acquireDynamic("node001");
if(dyn)
{
connect(dyn,SIGNAL(message(const QString&)),this,SLOT(messageLog(const QString &)));
qDebug()<<" dynamic get";
}
connect(nodePtr.data(),&Node01Replica::activedChanged,this,[this](){
qDebug()<<" state changed "<<this->nodePtr->actived();
ui->textBrowser->append(" state changed " +QString(this->nodePtr->actived()?" actived ":" inactived"));
});
}
//简单测试目标节点的功能是否可用
this->nodePtr->server_slot(false);
上述代码有少量测试逻辑,不影响整体流程。但是注意waitForRegistry是否需要取决于你是否会立即想调用远端的功能。因为后面我需要测试instances是否存在,所以会需要。实际测试,第一次调用这个函数,得到一个节点,第二次以后有两个(这个是正确的),所以链接建立是有延迟的。
进程B,尝试开始写文件
for(int i=0;i<1000;++i)
{
auto msg = "message "+ QByteArray::number(i)+"\n";
this->nodePtr->writeFile("./testfile.txt",msg);
}
this->nodePtr->writeFile("./testfile.txt","");
逻辑很简单,就是直接调用代理的函数。
![](https://img-blog.csdnimg.cn/img_convert/45c9f0d570977cd0d166ec0ac81e7ef6.png)
测试,左边的app,按server建立服务,右边依次按connect和write to,写数据。
![](https://img-blog.csdnimg.cn/img_convert/c17a83b1f7a5d9fb0e9ad55877e44a8b.png)
在左边的程序路径下,得到测试文件。
源码等待上传,使用的QT为6.3.2