Qt新的远程通信机制简介:Qt Remote Objects (QtRO)



  • 0_1524119285345_222.jpg
    Qt新的远程通信机制简介:Qt Remote Objects (QtRO)

    从5.10开始加入。QtRO是一种进程间通信机制(IPC)。通过socket,它能实现进程间甚至是不同计算机之间的远程对象交互功能。QtRO扩展了Qt原有的功能,使得远程通信更加方便。

    接下来是QtRO的简单介绍,对各个部分进行概述。

    QtRO封装了序列化和反序列化过程。用户可以假装有一个对象可供访问,而实际上这个对象存在于另一个进程,甚至另一台计算机中。
    QtRO最大的特点,是它是基于现有的Qt对象API机制(使用Q_PROPERTY, signals以及slots定义的API)。即,有几个默认前提:
    1、远程的对象必须继承自QObject
    2、使用meta-object的机制(包括property、信号和槽)进行互动

    说一句题外话,其实Qt在很多交互场合都使用了这套机制,比如QML Engine和C++交互,JS Engine和C++交互,Web Engine和C++互动,我猜今年推出的Python Engine和C++交互也会使用MOC。

    互动的进程称为Node,每个Node都是对等的。Node上可以包含真正的对象,称作Source;Node也可以包含一个镜像对象,称作Replica。多个Replica可以连接到一个Source,当Source的属性发生变化,都会通知每个Replica。反过来,Replica可以调用函数来改变Source的属性。(每个Node都是对等的,可以既包含Source又包含Replica)

    Replica是一个轻量级的代理(映射Source的状态),同时也是一个继承自QObject的对象,因此可以与本地对象连接信号和槽。也就是说,Replica可以作为信号和槽的转发器,用户使用时就像对待本地对象一样。
    除了Source的映射外,Replica还具备一些属性,用来判断连接状态和是否初始完成。
    为了最大程度节省流量,Replica和Source的通信是基于事件机制。因此在初始时需要对所有属性进行初始化同步。

    QtRO的Source设计时有主动推送变化的功能,而不需要客户端去查询并等待(传统RPC机制)。相对来说更省流量。它和传统RPC有很明显的区别:
    1、双向通信,基于事件机制
    2、完全面向对象而不是函数

    接下来是技术细节介绍。(如果有兴趣可以往下看,内容有些枯燥,也可以参考官方的手册)

    Replica的使用。有两种方法可以获得Replica:
    1、编译时加入
    编译时加入的Replica继承自QRemoteObjectReplica。REPC生成器会自动生成Replica的定义文件(一个头文件)。在pro文件中加入REPC_REPLICA宏可以把生成过程集成到项目编译过程中去。Replica是一个完整的类型(不是虚类),但是没有公开的构造函数,用户需要使用QRemoteObjectNode::acquire模板函数来实例化Replica。
    2、动态获取
    动态获取的Replica继承自QRemoteObjectDynamicReplica。它由非模板函数QRemoteObjectNode::acquire()生成,必须提供一个Source名称。动态Replica使用起来可能有点罗嗦,但是不需要编译时的头文件。并且在QML或者一些脚本语言中使用起来更方便。动态Replica不支持初始化值,在初始化完成前也不能使用。
    以上两种实现方式的一个很重要的区别,就在于未初始化时的状态。因为一个动态Replica只有在初始化后才能获取metaObject,因此在初始化前它基本上没有API可用。反过来,编译时加入的Replica已经具备metaObject,因此在未初始化前它已经有完整的API。用户甚至可以指定属性的默认值,这些默认值可以一直用到Replica被远程Source初始化。

    Source是一个对象。它挂载到Node上,包含有公开API。
    你可以选择使用一个QObject的类直接作为Source,也可以选择在一个.rep文件中定义需要的API,交由REPC生成器生成模板。
    如果你已经有一个完整的QObject类,可以把它传给QRemoeObjectHostBase:: enableRemoting()函数,让它成为一个Source。
    你可以让REPC生成器为你生成Source,即一个定义了API的头文件(在pro文件中使用REPC_SOURCE宏)。有三个选择去实现这些API(假设你的Source类的名字叫做Foo):
    1、继承FooSimpleSource
    在头文件里,定义了一个<Type>SimpleSource类。它给每个属性提供最基本的getter/ setter方法。这里的<Type>代表了.rep文件中Source类名称。即如果.rep文件中定义的类名称叫做“MyType”,那么在生成的头文件中就会有一个MyTypeSimpleSource的类。最简单快速的方法就是继承这个类,并且实现这个类中的API(都是纯虚函数)。你也可以Override任何需要的属性和函数。
    2、继承FooSource
    如果你需要隐藏实现的细节,那么你可以使用<Type>Source类。在生成的头文件中,它是第二个定义的类。这个类的定义没有包含任何数据对象,getter/setting函数也变成了纯虚函数。继承该类,用户有更多的灵活性去实现这个类,当然用户要写更多的代码。
    3、在用户自己的QObject对象里实现FooSourceAPI
    最后这个办法,<Type>SourceAPI类是一个模板类,专门给QRemoeObjectHostBase:: enableRemoting()的模板函数使用。它让你可以使用任意QObject子类(必须含有API)作为Source。如果这个类没有提供正确的API,你会收到编译时的警告。使用这个类让你可以隐藏或变换属性或信号槽。

    在每个端点可以存在许多Source,因此每个Source需要一个独一无二的名称。所有的REPC生成的头文件都有一个方式来确定名称(Q_CLASSINFO宏,用于replica/ simplesource/source,对于SourceAPI使用一个静态的name()函数)。如果你使用自己的QObject子类作为Source(使用QRemoteObjectHostBase::enableRemoting),那么Source的名字使用如下逻辑来决定:
    1、如果该类或者任何它的父类的Q_CLASSINFO序列中定义过RemoteObjectType,那么QRemoteObjectHostBase::enableRemoting中传入的name参数将被使用。
    2、否则,QObject的objectName被使用
    3、如果以上都失败,QRemoteObjectHostBase::enableRemoting返回错误。

    注意:QObject层面的API不会同步给Replica。比如,Replica有一个destroyed信号,它不会触发source的destroyed信号,反之亦然。因为Source和每个Replica都是一个独立的QObject。同步的API在.rep模板文件中定义,或者对于直接使用的QObject的情况,同步的API是在QObject的继承链上的元素。除非在某个父类中定义了Q_CLASSINFO("RemoteObject Type")。如果定义过Q_CLASSINFO("RemoteObject Type")那么那个父类是API检索中最低层。

    Node的使用。Node是进程间通信的平台和基础。所有QtRO的交互数据都被Node封装成尽可能小的独立数据包。
    每个使用QtRO的进程都必须实例化一种Node类(可以是QRemoteObjectNode,QRemoteObjectHost或者是QRemoteObjecRegistryHost)。后面几种Node提供了更多功能。QRemoteObjectHost和QRemoteObjecRegistryHost都支持enableRemoting()/disableRemote()函数,用来把Source对象发布到网络中去。如果要使用Registry功能,网络上必须要有一个QRemoteObjectRegistryHost的Node。然后其他的Node可以传递RegistryHost的URL参数到Node的registryAddress构造函数,或者传送URL参数到setRegistryUrl()函数。
    QtRO是P2P的网络。为了acquire()一个有效的Replica,承载Replica的Node必须连接到带Source的Node。Host Node允许其他Node通过设置独一无二的地址连接到它(地址传递到其他Node的构造函数,或者是使用setHostUrl方法)。Replica所在的Node必须建立与Host Node的连接,才能初始化Replica并保持数据一致。

    使用QtRO的URL连接Node
    Host Node使用用户自定义的URL来简化连接。目前QtRO支持两种连接方式(很有可以会扩展)。一个是使用标准TCP/IP的连接,它即支持同一台设备不同进程间通信,也支持不同设备通信。另一种使用local连接,它协议简单,具体实现依赖于OS特性。但不支持设备间通信。
    使用local连接时,必须使用一个独一无二的名字。使用TCP连接时,必须使用一个独一无二的IP和Port的组合。
    目前QtRO不支持zeroconf(零配置网络规范)。因此所有Node必须事前直销所有连接配置。QRemoteObjectRegistry用来简化连接过程,特别是对与有多个Host Node的网络。
    连接类型总结如下表:

    URL Host Node Connecting Node
    QUrl("local:replica") QLocalServer("replica") QLocalSocket("replica")
    QUrl("tcp://192.168.1.1:9999") QTcpServer("192.168.1.1",9999) QTcpSocket("192.168.1.1",9999)

    Node有多个enableRemoting()函数,用来在网络上共享对象(如果Node不是Host Node则会报错)。其他设备/进程,如果需要与其交互,可以使用Node的acquire()函数来实例化Replica。



  • 首发最新模块的介绍!很棒啊。强烈支持一个!而且我觉得这个技术,和微软的COM组件对象编程很相似,值得我们去推广使用!🤑 😛 😛



  • 没写完呢。
    这个对Qter非常有用。
    其他平台其实都有类似实现,其实Qt平台上已经有点晚了。



  • @linbin823 这种情况是晚了一些,大家都在使用COM技术,还有各种脚本语言的实现。现在推广来说稍微难了。



  • 期待下文,最好能做个Demo



  • 这东西是不是相当于java的RMI?都是十几年前的东西了,为什么Qt要到现在才开始提供?而且我记得我面试Java工作的时候,RMI和序列化数据也是一个重点,Qt就一点感觉都没有吗?

    我还有一个疑惑很多年了,从学Qt的时候一直到现在:就是J2SE=Qt桌面版,J2ME=Qt嵌入式,但是J2EE为什么在Qt里没有相应的东西?Java真正能火起来其实是靠J2EE,后来又攀上了Android这颗大树。但是桌面版和嵌入式,绝对是Qt完胜。



  • @stlcours 这也不是什么很复杂的东西。QtRO解决的是远程信号槽以及属性的传递。都是非常上层的封装。

    Qt不做服务端,有好多技术和历史因素,毕竟它一路走来都是定位客户端和界面的开发框架。

    当然github上也有用Qt开发的服务端软件,有些benchmark分数还挺高。这个就只能说有需求就去玩玩,但估计成不了主流。



  • @linbin823 能举几个例子吗?有哪些Qt开发的服务端软件?是不是主流不要紧,关键是给我们这些人使用方便啊。



  • @stlcours
    做httpserver:tufao,cutelyst
    这两天还看到一个https://github.com/hgoldfish/qtnetworkng



  • 谢谢Linbin。虽然qtnetworkng是全英文文档,但是例子居然是"https://news.163.com,看来是国人的作品啊!!


 

最近的回复

  • 0_1539655233041_329385492.jpg
    各位大家好哦。
    经过一个晚上对论坛的维护更新,我们成功地将论坛更新到最新的版本。这次的版本更新主要是提升了稳定性,因此功能上和原来的一样,请大家放心,不会出现奇怪的东西什么的啦。然后呢,由于提升了稳定性,因此论坛不会莫名其妙地出现未响应的情况,总之比以前好就对啦。

    此外,论坛新增了自己研制的一个分享的插件:nodebb-plugin-share-sns-cn。这个插件呢,主要是做主流社交网站的分享的。目前暂时支持QQ、微博以及微信的分享。目前初版是1.0。

    希望大家常常来论坛玩~

    阅读更多
  • 发一些二次元妹子图吧。谁叫我有时候也挺无聊的呢~
    0_1539486167719_119.jpg

    阅读更多
  • 0_1539445819168_123.jpg
    各位大家好!
    又有一段时间啦。我们的萌梦动作编辑器又进行更新啦!☺ ☺
    这次的更新呢,主要是完善了一下自动构建系统。也就是说为了持续构建并且能够给到爱好者可用并且稳定的软件,我们做了很多的努力,其中一点就是制作了脚本进行持续构建,以前要花很多很多的时间对动作编辑器进行构建,现在呢,没有那么复杂了。简简单单地数个批处理操作,即可快速构建咱们的动作编辑器,使得从代码到产品只需要十几分钟即可完成,比以前的速度节省了90%的时间,可谓神速!这套系统已经成功地应用到Windows平台线上了,现在基本可以做到每日构建的水平。而且为每次构建专门准备了pdb文件,即一旦用户运行动作编辑器出现了问题,可以通过程序调试数据库定位到问题的位置,从而提升服务的水平,想想这对于我一直坚持的萌梦这款产品来说算是革命性的操作呢。

    当然现在这套系统只能应用在Windows线上,Mac OS X这条线是我接下来努力的方向,也希望能够实现Mac这条线的每日构建效果。然后Linux平台也比较重要,想起Linux开发应该比Windows等等要方便,所以我想难度比Windows的要小很多,甚至比Mac OS X的都要小。总之路途还很遥远,加油努力吧!

    这次的Windows版本已经成功地在Windows的安装包中进行测试,同时提供在线的和离线的安装包。下面就是这些安装包的github下载地址。

    Windows在线安装包
    Windows离线安装包

    阅读更多
  • 手动给一个赞!!☺ 🤗
    0_1539264026793_好美的图啊.jpg

    阅读更多

关注我们

微博
QQ群











召唤伊斯特瓦尔