如何在QtRemoteObject远程节点/副本断开时向主机发送信号
QtRemoteObject 检测客户端节点/副本断开连接的实现方案
QtRemoteObject(QtRO)原生并未提供直接的信号机制用于通知主机或注册表客户端节点/特定副本的连接断开,但可以通过以下几种替代方案实现需求:
1. 轮询注册表的源列表
QRemoteObjectRegistry维护了当前可用的远程源列表,可通过定期对比列表变化来检测源的消失(对应客户端断开时注销源的操作):
// 初始化时记录初始源列表 QStringList lastSourceList = registry->sourceList(); // 启动定时轮询 QTimer* checkTimer = new QTimer(this); connect(checkTimer, &QTimer::timeout, this, [=](){ QStringList currentList = registry->sourceList(); // 筛选出已消失的源 QStringList disconnectedSources; for (const QString& source : lastSourceList) { if (!currentList.contains(source)) { disconnectedSources.append(source); } } if (!disconnectedSources.isEmpty()) { qDebug() << "检测到源断开:" << disconnectedSources; lastSourceList = currentList; } }); checkTimer->start(1000); // 可根据需求调整轮询间隔
注意:该方法只能检测源的消失,无法直接关联到特定客户端节点,且存在一定检测延迟。
2. 自定义心跳机制(精准检测特定副本)
在自定义的远程源类中添加心跳逻辑,客户端定期发送心跳包,主机端通过超时未收到心跳来判定副本断开:
1)在rep文件中定义心跳接口
class HeartbeatSource { SIGNAL(heartbeat(qint64 clientId)) };
2)主机端实现心跳检测
// 存储客户端ID与最后心跳时间戳的映射 QMap<qint64, qint64> clientHeartbeatMap; // 启动心跳检查定时器 QTimer* heartbeatCheckTimer = new QTimer(this); connect(heartbeatCheckTimer, &QTimer::timeout, this, [=](){ qint64 currentTime = QDateTime::currentMSecsSinceEpoch(); QList<qint64> disconnectedClients; // 遍历检查超时的客户端 for (auto it = clientHeartbeatMap.begin(); it != clientHeartbeatMap.end(); ++it) { if (currentTime - it.value() > 3000) { // 3秒未收到心跳判定断开 disconnectedClients.append(it.key()); } } // 处理断开逻辑 for (qint64 clientId : disconnectedClients) { qDebug() << "客户端" << clientId << "已断开"; clientHeartbeatMap.remove(clientId); } }); heartbeatCheckTimer->start(1000); // 接收心跳并更新时间戳 connect(heartbeatSource, &HeartbeatSource::heartbeat, this, [=](qint64 clientId){ clientHeartbeatMap[clientId] = QDateTime::currentMSecsSinceEpoch(); // 首次收到心跳可判定为新客户端连接 if (clientHeartbeatMap.count(clientId) == 1) { qDebug() << "客户端" << clientId << "已连接"; } });
3)客户端副本发送心跳
QTimer* heartbeatTimer = new QTimer(this); // 生成唯一客户端ID qint64 clientId = QRandomGenerator::global()->generate(); connect(heartbeatTimer, &QTimer::timeout, this, [=](){ replica->heartbeat(clientId); }); heartbeatTimer->start(1000); // 每秒发送一次心跳
优点:可精准定位到特定副本的连接状态,延迟可控;缺点:需要自定义业务逻辑,增加代码复杂度。
3. 监听底层Socket状态(仅TCP传输可用)
QtRO基于Qt Network实现,主机端的QRemoteObjectHost底层使用QTcpServer,可通过重写incomingConnection方法获取客户端Socket,监听其断开信号:
class CustomRemoteObjectHost : public QRemoteObjectHost { protected: void incomingConnection(qintptr socketDescriptor) override { QTcpSocket* socket = new QTcpSocket(this); if (socket->setSocketDescriptor(socketDescriptor)) { // 监听Socket断开 connect(socket, &QTcpSocket::disconnected, this, [=](){ qDebug() << "检测到客户端Socket断开,描述符:" << socketDescriptor; socket->deleteLater(); }); // 监听Socket连接 connect(socket, &QTcpSocket::connected, this, [=](){ qDebug() << "客户端Socket已连接,描述符:" << socketDescriptor; }); } QRemoteObjectHost::incomingConnection(socketDescriptor); } };
优点:可实时检测Socket层面的连接变化;缺点:无法直接关联到QtRO的源或副本,仅支持TCP传输方式。
目前QtRO官方文档(QRemoteObjectRegistry、QRemoteObjectNode、QRemoteObjectHost)中确实未提供直接的连接/断开通知信号,这也是开发者普遍遇到的痛点。
内容的提问来源于stack exchange,提问作者enricop
相关产品推荐
相关产品推荐

