当前位置: 首页 > article >正文

QModbusTCPClient 服务器断开引起的程序崩溃

最近使用QModbusTCPClient 与一套设备通信,有一个QTimer频繁的通过读取设备寄存器。程序运行良好,但是有个问题:正常进行中设备断电了,整个程序都会崩溃。解决过程如下:

1.失败方案一

在QModbusTCPClient的errorOccurred()信号中判断错误后及时关闭QTimer,避免出错之后还要频繁访问。

此方案失败,问题不在这里。

2.失败方案二

m_reply= m_modbus->sendReadRequest(m_unit[1], m_outputID);
        if(m_reply && !m_reply->isFinished())
        {
            connect(m_reply, &QModbusReply::finished, [this]() {
                if(m_reply->error() == QModbusDevice::NoError)
                {
                    m_outputs = m_reply->result().values();
                }
                m_modbus->disconnect(SIGNAL(timeoutChanged(int)), 0, 0);
                delete m_reply;
                m_reply = nullptr;
            });
        }

一个典型的应用如上。对 QModbusTCPClient发送读写请求后,会得到一个QModbusReply指针,根据QModbusReply的finished信号判断请求结果。这个过程是异步的,所以上面的及时停止QTimer并不能真的“及时”停止。

在finished的响应槽函数中判断一下error状态,再进行后面的操作,仍然失败。

3.方案三(有点眉目)

m_reply= m_modbus->sendReadRequest(m_unit[1], m_outputID);
        if(m_reply && !m_reply->isFinished())
        {
            connect(m_reply, &QModbusReply::finished, [this]() {

            });
        }

直接把这个函数体变成空的,什么也不做,发现程序不崩溃了。问题范围成功缩小。于是对函数体中的逐行打印,看看到底哪一步崩溃的。

m_reply= m_modbus->sendReadRequest(m_unit[1], m_outputID);
        if(m_reply && !m_reply->isFinished())
        {
            connect(m_reply, &QModbusReply::finished, [this]() {
qDebug()<<1;
                if(m_reply->error() == QModbusDevice::NoError)
                {
qDebug()<<2;
                    m_outputs = m_reply->result().values();
                }
qDebug()<<3;
                m_modbus->disconnect(SIGNAL(timeoutChanged(int)), 0, 0);
qDebug()<<4;
                delete m_reply;
qDebug()<<5;
                m_reply = nullptr;
            });
        }

结果发现只要对m_reply进行访问,就会崩溃。 

4.方案四(部分解决)

在reply的finished信号响应函数中为啥不能访问reply呢,打印一下reply看看啥情况。

m_reply= m_modbus->sendReadRequest(m_unit[1], m_outputID);
        if(m_reply && !m_reply->isFinished())
        {
            connect(m_reply, &QModbusReply::finished, [this]() {

qDebug()<<m_reply;
            });
        }

一旦QModbus设备断电,reply竟然是空值!!!!

QModbusReply(0x232d1d5de40)
QModbusReply(0x232d1d5f3b0)
QModbusReply(0x232d1d604e0)
QModbusReply(0x232d1d60f00)
QModbusReply(0x232d1d630e0)
"TCP socket error (The remote host closed the connection)."
QModbusDevice::UnconnectedState
QObject(0x0)
QObject(0x0)
QObject(0x0)
QObject(0x0)
QObject(0x0)

远程服务器关闭之后,reply的响应函数中访问reply竟然是空值!!!

所以在响应函数中还要判断reply是都为空值,才能继续:

m_reply= m_modbus->sendReadRequest(m_unit[1], m_outputID);
        if(m_reply && !m_reply->isFinished())
        {
            connect(m_reply, &QModbusReply::finished, [this]() {

               if (!m_reply[0]) {
                    return ;
                }

                if(m_reply->error() == QModbusDevice::NoError)
                {
                    m_outputs = m_reply->result().values();
                }
                m_modbus->disconnect(SIGNAL(timeoutChanged(int)), 0, 0);
                delete m_reply;
                m_reply = nullptr;
            });
        }

这样处理后程序终于正常了,但是又出现了另一个问题。

5.方案五(完整解决)

上述方案中使用deleter m_reply竟然也有问题。当本来通信超时的时候(比如传入的错误的通信地址),响应会比较慢。此时服务器断开连接,reply竟然不是nullptr,此时程序在delete reply这句崩了。怀疑此时的reply还在异步处理别的事情。改成reply->deleterLater()之后就没问题了。

还有一个隐藏的问题,m_modbus->disconnect(SIGNAL(timeoutChanged(int)), 0, 0) 这句是为了解决内存增加问题,如果服务器中断导致reply==nullptr,这句话就被跳过了。可以把这句放在函数体最前面,并没有导致问题。

完整解决后如下:

m_reply = m_modbus->sendReadRequest(read, m_485ID);

        if(m_reply && !m_reply->isFinished())
        {
            connect(m_reply, &QModbusReply::finished, [this]() {

                m_modbus->disconnect(SIGNAL(timeoutChanged(int)), 0, 0);

                //如果远程服务器关闭,这个reply是0
                if (!m_reply) {
                    return ;
                }

                if(m_reply->error() == QModbusDevice::NoError)
                {

                    QVector<quint16>values = m_reply->result().values();
                    if(m_values != values) {
                        m_values = values;
                    }
                }

//如果超时错误,下面不能直接delete,否则服务中断仍然崩溃
                m_reply->deleteLater();
                m_reply = nullptr;

            });
        }
        else {
            m_modbus->disconnect(SIGNAL(timeoutChanged(int)), 0, 0);
            m_reply->deleteLater();
            m_reply = nullptr;
        }

目前算是彻底解决崩溃问题,后面继续测试。


http://www.kler.cn/a/518253.html

相关文章:

  • 996引擎 - 前期准备-配置开发环境
  • 第20篇:Python 开发进阶:使用Django进行Web开发详解
  • SpringCloud两种注册中心
  • Android GLSurfaceView 覆盖其它控件问题 (RK平台)
  • Linux C openssl aes-128-cbc demo
  • StarRocks常用命令
  • ChirpIoT技术的优势以及局限性
  • Spring Boot - 数据库集成03 - 集成Mybatis
  • SSM框架探秘:Spring 整合 Mybatis 框架
  • Linux(Centos 7.6)命令详解:wc
  • linux查看上次开机时间
  • Effective C++ 规则46: 需要类型转换时,请为模板定义非成员函数
  • LVGL+FreeRTOS实战项目:智能健康助手(xgzp6847a篇)
  • 【算法工程】VS Code问题解决:Failed to parse remote port from server output
  • Java多线程的面试面试题及答案解析
  • Golang之Context详解
  • 【pytorch 】miniconda python3.11 环境安装pytorch
  • 无公网IP 外网访问媒体服务器 Emby
  • GS论文阅读--GeoTexDensifier
  • 如何实现分页相关功能
  • 比简单工厂更好的 - 工厂方法模式(Factory Method Pattern)
  • Lambda 表达式
  • 笔记《Effective Java》01: 创建和销毁对象
  • 软件测试丨消息管道(Kafka)测试体系
  • 电路研究9.2.1——合宙Air780EP音频的AT控制指令
  • 【工程篇】01:GPU可用测试代码