欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 资讯 > QModbusTCPClient 服务器断开引起的程序崩溃

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

2025/1/31 8:34:36 来源:https://blog.csdn.net/weixin_69505365/article/details/145341129  浏览:    关键词: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是0if (!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;}

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

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com