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;



ma​​in.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),但如果您的waitForBytesWrittenwaitForReadyRead 调用成功,那么您永远不会调用disconnectFromHost。因此mpSocket-&gt;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 在工作进程中连续写入。避免内存泄漏的最佳实践的主要内容,如果未能解决你的问题,请参考以下文章

无法写入 QRunnable 内的 QTcpSocket

QTcpSocket的连续发送数据和连续接收数据

QTcpSocket::write - 如何写入大文件?

在 C++ 中使用 popen 连续写入子进程

Qt QTcpSocket 异步写入

QTcpSocket 写入数据结构