QTcpSocket 在工作进程中连续写入。避免内存泄漏的最佳实践
Posted
技术标签:
【中文标题】QTcpSocket 在工作进程中连续写入。避免内存泄漏的最佳实践【英文标题】:QTcpSocket continuous write in worker process. Best practice to avoid memory leaks 【发布时间】:2021-07-06 16:36:59 【问题描述】:我必须在特定的时间间隔内通过 TCP 套接字向服务器发送单个小数据包。我正在使用 Qt 进行开发。 我的想法是创建从 QObject 继承的类,将 Socket 写入部分放在此类的无限循环中,并将其作为单独的线程执行。 它可以工作,但是会泄漏内存....此时我很迷茫。 这是我到目前为止所做的(某种伪代码......):
Task_SendPacket.h
#ifndef TASK_SENDPACKET_H
#define TASK_SENDPACKET_H
#include <QObject>
#include <QTcpSocket>
class Task_SendPacket : public QObject
Q_OBJECT
public:
explicit Task_SendPacket(QObject *parent = nullptr);
public slots:
void startTask();
void _connected();
void _readReady();
void _error(QAbstractSocket::SocketError _Error);
signals:
private:
QTcpSocket *mpSocket;
char SomeData[100];
;
#endif // TASK_SENDPACKET_H
Task_SendPacket.cpp
#include "Task_SendPacket.h"
#include <QThread>
#include <QDebug>
Task_SendPacket::Task_SendPacket(QObject *parent)
: QObject(parent)
void Task_SendPacket::startTask()
while(true)
mpSocket = new QTcpSocket(this);
connect(mpSocket, &QTcpSocket::disconnected, mpSocket, &QTcpSocket::deleteLater);
connect(mpSocket, &QTcpSocket::connected, this, &Task_SetSelectedCamera::_connected);
connect(mpSocket, &QTcpSocket::readyRead, this, &Task_SetSelectedCamera::_readReady);
connect(mpSocket, &QTcpSocket::errorOccurred,this, &Task_SetSelectedCamera::_error);
mpSocket->connectToHost("192.168.0.1", 50100, QTcpSocket::ReadWrite);
if(!mpSocket->waitForConnected(Protocol::NetworkTimeout_ms))
qWarning() << "SS Packet send - Connection timeout";
if(!mpSocket->waitForBytesWritten(Protocol::NetworkTimeout_ms))
qWarning() << "SS Packet send - write timeout";
mpSocket->disconnectFromHost();
if(!mpSocket->waitForReadyRead(Protocol::NetworkTimeout_ms))
qWarning() << "SS Packet send - read timeout";
mpSocket->disconnectFromHost();
QThread::msleep(1000);
void Task_SetSelectedCamera::_connected()
if(mpSocket->write(SomeData, 100) == -1)
qWarning() << "Write Header error ";
;
void Task_SetSelectedCamera::_readReady()
char DataRead[100] = mpSocket->readAll();
/* Do stuff with DataRead...*/
mpSocket->disconnectFromHost();
void Task_SetSelectedCamera::_error(QAbstractSocket::SocketError _Error)
qWarning() << _Error;
main.cpp
/* Start Task SetSelectedCamera */
QThread *ServiceThread1 = new QThread();
Task_SendPacket *oTaskSendPacket = new Task_SendPacket();
oTaskSendPacket->moveToThread(ServiceThread1);
QObject::connect(ServiceThread1, &QThread::finished, oTaskSendPacket, &QObject::deleteLater);
QObject::connect(ServiceThread1, &QThread::started, oTaskSendPacket, &Task_SendPacket::startTask);
ServiceThread1->start();
qInfo() << "Task SendPacket started";
如果我让应用程序运行几天,它会增加内存并崩溃。 这是创建新 QTcpSocket、连接它并发送数据的正确方法吗?
谢谢
编辑 我编辑了原始帖子,因为我没有关注正确的观点。在我的想法中,新的 QTcpSocket(...) 必须在 while 循环的每次迭代中运行,因为我希望 mpSocket 在每次断开连接时都被标记为删除。 在第二个实验中(在下面报告)我试图在 while 循环之前移动套接字部分......它仍然有效。
mpSocket = new QTcpSocket(this);
connect(mpSocket, &QTcpSocket::disconnected, mpSocket, &QTcpSocket::deleteLater);
connect(mpSocket, &QTcpSocket::connected, this, &Task_SetSelectedCamera::_connected);
connect(mpSocket, &QTcpSocket::readyRead, this, &Task_SetSelectedCamera::_readReady);
connect(mpSocket, &QTcpSocket::errorOccurred,this, &Task_SetSelectedCamera::_error);
while(true)
mpSocket->connectToHost("192.168.0.1", 50100, QTcpSocket::ReadWrite);
if(!mpSocket->waitForConnected(Protocol::NetworkTimeout_ms))
qWarning() << "SS Packet send - Connection timeout";
if(!mpSocket->waitForBytesWritten(Protocol::NetworkTimeout_ms))
qWarning() << "SS Packet send - write timeout";
mpSocket->disconnectFromHost();
if(!mpSocket->waitForReadyRead(Protocol::NetworkTimeout_ms))
qWarning() << "SS Packet send - read timeout";
mpSocket->disconnectFromHost();
QThread::msleep(1000);
所以现在的问题是:
-
每次迭代都创建一个新的 QTcpSocket 是否正确?
为什么断开连接后socket仍然工作(因为我这样做
收到断开连接...我跟踪到了)
【问题讨论】:
在Task_SetSelectedCamera::startTask
中查看你的逻辑。您每次循环迭代都执行mpSocket = new QTcpSocket(this)
,但如果您的waitForBytesWritten
和waitForReadyRead
调用成功,那么您永远不会调用disconnectFromHost
。因此mpSocket->deleteLater
永远不会被调用。因此,您可能会在每次循环迭代时泄漏QTcpSocket
。
ReadyRead结束时Socket不是断开了吗?如果这两个成功,我希望在阅读响应消息后断开套接字,这是该过程的最后一步。
抱歉,我错过了_readReady
中的disconnectFromHost
电话。即便如此,最新的编辑还是有点混淆了这个问题。您可以编辑问题以显示当前代码和问题症状吗?
其实第一个版本就是我面临的泄漏问题。编辑...只是更改了套接字的创建以尝试不在每次迭代时创建新的套接字,但我确信它不起作用,因为我认为套接字被删除(或标记为删除)...但是确实它正在运行。我现在让它通宵运行,看看它是否泄漏。这里的重点是它为什么有效?恐怕我误解了 QTcpSocket 的生命周期
我可以确认,在 while 循环之前创建套接字,应用程序不会泄漏!为什么会起作用?
【参考方案1】:
真丢脸! 我在很多方面都错了,但请原谅我,我很年轻而且很花哨。 我想在这里发布我所犯的错误,希望它可以帮助其他人,因为我认为我已经得到了这个错误的解决方案,受我之前在 RTOS 编程方面的经验的影响。
首先 永远不要在 QThread 或 moveToThread(...) 到 QThread 的 QObject 中放置无限 while 循环。这是因为 while 循环会阻止 QEventLoop 运行。一切都会卡在里面
while(true)
...
应用程序的任何生命周期(包括信号和插槽)都不会继续!然后 - 信号/插槽卡住 我应该怀疑一些事情,因为没有任何东西都没有问题
...
if(!mpSocket->waitForBytesWritten(Protocol::NetworkTimeout_ms))
qWarning() << "SS Packet send - write timeout";
mpSocket->disconnectFromHost();
if(!mpSocket->waitForReadyRead(Protocol::NetworkTimeout_ms))
qWarning() << "SS Packet send - read timeout";
mpSocket->disconnectFromHost();
...
没有ReadyRead 信号,没有BytesWritten 信号等等。这是(显然......现在)因为这些函数正在阻塞,并且由于我的无限循环阻塞了信号/插槽逻辑,所以没有其他方法可以让我的 QTcpSocket 工作。如果应用程序生命周期正常,信号和插槽就可以工作,否则......它们不会。最后 - deleteAater() 不工作 我不明白的最后一件事是为什么,把我的
mpSocket = new QTcpSocket(this);
在 while 循环之前(应该在每次迭代中删除套接字,套接字仍然可以工作。 这也是因为如果 QEventLoop 不起作用,则套接字的 deleteLater() 将不起作用......顺便说一下,这也是(我认为)我的应用程序泄漏内存的原因! 现在我用 QTimer 逻辑重新实现了它,它工作正常。 我认为,我被骗的原因是因为来自 RTOS 的基本思想是在每个线程中都有一个初始化部分和一个循环部分。这就是为什么我一直在寻找无限循环...对于如何在没有 QThread 的情况下创建无限循环仍然存在疑问:
-
搞砸应用程序逻辑
使用 QTimer
谢谢
【讨论】:
以上是关于QTcpSocket 在工作进程中连续写入。避免内存泄漏的最佳实践的主要内容,如果未能解决你的问题,请参考以下文章