使用QtRemoteObject时,有一些坑要注意一下。

1.服务端的url使用问题

关于是使用"local:myName"还是使用"tcp://127.0.0.1:123"的问题。其实这两个分别走的是命名管道/UDS和tcp。使用local的话,由于是本地上的资源,不用经过网络层,速度相对很快。走tcp的话,走了网络层,就慢了些。
假如服务端和客户端都在一个电脑上的话,用local还是tcp问题不大;但是不在同一台电脑上的话,只能用tcp了。

2.python线程问题

我在python中建立一个QtRemoteObject的服务端,然后在QThread中定时通过client发送信号出来

    while True:
        callback(url)
        # time.sleep(1)
        QThread.msleep(1000)

假如是用

remoteHost = QRemoteObjectHost(QUrl("local:replica"))

这样子会出现卡死的问题(初次连接连得上但是信号、槽都不好使,关掉客户端,服务端好像也会随着挂掉)
但是使用这个

remoteHost = QRemoteObjectHost(QUrl("tcp://127.0.0.1:123"))

就不会,很奇怪。

后来发现了,原因是在主线程创建的QtRo对象,不能在子线程直接调用(比如发出信号);需要通过一个中间对象经由信号槽的方式把数据传递出来給主线程来执行。

from PySide2 import QtCore
from PySide2.QtCore import QObject, Slot, Signal, QMetaObject, QThread, SIGNAL, SLOT
from PySide2.QtCore import Qt, QGenericArgument

class MyObject(QObject):
    def __init__(self):
        super().__init__()
    # 内部信号
    mSignal = Signal("QVariant")

class Bridge(QObject):
    # 用来发送信息到C++那边去,主要是告知此时的状态
    serverToClient = Signal("QVariant")

    def __init__(self, proFunc):
        super(Bridge, self).__init__()

        self.func = proFunc
        
        self.mObj = MyObject()
        self.mObj.mSignal.connect(self.serverToClient)

    # 这个是供c++那边直接调用的槽函数,可以用来设置参数,也可以通过返回值读取参数。
    # 这里只是提供基础的数据通讯,复杂的通讯可以在这个基础上实现自己的封装
    # The slot exposed to a remote client.
    @Slot("QVariant", result="QVariant")
    def clientToServer(self, cmd):
        ret = self.func(cmd)
        return ret

    def sendMessage(self, val):
        self.mObj.mSignal.emit(val)

3.状态线程与读写线程要分开

在使用动态Replica,假如想在连接上之后就立马调用带返回值的槽函数,最好是使用Qt::QueuedConnection 来连接stateChanged。用DirectConnection会出问题。

mReplica = mRepNode->acquireDynamic("MySource"); // acquire replica of source from host node
    connect(mReplica, &QRemoteObjectReplica::initialized, [=](){
        qDebug() << "initialized----------------------" << QDateTime::currentDateTime();
    });

    //用Qt::QueuedConnection才能很好地进行操作(调用带返回值的槽函数)
    connect(mReplica, &QRemoteObjectReplica::stateChanged, this, [=](QRemoteObjectReplica::State state, QRemoteObjectReplica::State oldState){
        qDebug() << oldState << state << QDateTime::currentDateTime();
        if(state == QRemoteObjectReplica::Valid)
        {
            QRemoteObjectPendingCall val;

            qDebug() << "call:" << val.isFinished() << val.error();

            QMetaObject::invokeMethod(mReplica, "getFileList", Qt::DirectConnection, Q_RETURN_ARG(QRemoteObjectPendingCall, val));

            qDebug() << "call:" << val.isFinished() << val.error();
            val.waitForFinished(1000);

            QVariantList infoList = val.returnValue().toList();
            qDebug() << infoList;
        }

    }, Qt::QueuedConnection);

或者也可以使用QTimer::singleShot来避免在函数中直接执行

4.无法连接QtRemoteObject的服务端

在检查了url(端口、IP)、服务端是否开启、rep文件是否一致之后,检查是不是开了代理

5.执行槽函数的过程中客户端断开会导致服务端崩溃

假如服务端有一个槽函数funcA,执行这个函数需要10s才能返回;然后QtRO客户端通过replica来执行这个函数时,再第5秒时断开了连接。那么,当服务端的这个函数返回时,就会往一个空的IO对象里面写东西,从而导致段错误,从而导致服务端崩溃。(但是得配合qApp->processEvents()才会触发)
会定位到void QRemoteObjectSourceIo::onServerRead(QObject *conn)

比如我在对应的槽函数中这样延时:
在这里插入图片描述

在这里插入图片描述
但是,使用qApp->processEvents()会导致的问题应该不止这个,其会严重打乱程序的逻辑顺序。因此,只有在真的是走投无路、山穷水尽、穷途末路、万不得已的情况下,才能孤注一掷地使用它。否则,千万不要使用。


参考:
【QtRO简介】
【Qt Remote Object(QtRO)实现进程间通信】

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐