Qt的QNetworkAccessManager的使用和防止内存泄漏

62 篇文章 8 订阅

先说结论,升级QT到最新版本即可解决!确认官方bug!

折腾了四五天了,最后在Qt论坛找到了答案,确认有问题的版本为qt-opensource-windows-x86-5.14.1.exe:

确认已经修复问题的版本qt-opensource-windows-x86-5.12.10和qt-opensource-windows-x86-5.9.9.exe:

因为5.9.9和5.14.1关于的版本显示一致,所以通过安装文件的名字区分比较好!

===============分割线===================

使用范例官方就有,但是官方的范例并不适合反复调用,比如我每隔1秒钟就要调用一次,一旦次数过多,时间过久,则会出现明显的内存泄露,最终导致应用崩溃。

那么这个问题我是如何解决的呢?在搜索了网上许多参考资料之后,(2021.4.1更新测试结果)仍然没能彻底解决问题,只是使得内存泄露的情况缓解了许多。

那么,先来看看代码:

//author:autumoon
//联系QQ:4589968
//日期:2021-03-31
 QString strCmdUrl = QString("http://") + strHost + ":" + QString::number(9998);

    QNetworkRequest request(strCmdUrl);
    setRequestHeader(request);

    static QNetworkAccessManager* naManager = new QNetworkAccessManager;

    QNetworkReply* reply = naManager->post(request, baPostData);
    QEventLoop eventloop;
    QMetaObject::Connection conRet = QObject::connect(reply, SIGNAL(finished()), &eventloop, SLOT(quit()));
    Q_ASSERT(conRet);

    QTimer timer;
    timer.singleShot(nTimeoutSeconds * 1000, &eventloop, SLOT(quit()));
    timer.start();

    eventloop.exec(QEventLoop::ExcludeUserInputEvents);

    if (timer.isActive())
    {
        QByteArray baReply;
        if (parseReplyData(reply, baReply) == 0)
        {
            reply->abort();
            reply->deleteLater();
            //naManager->deleteLater();
            baAnswer = baReply;
            return 0;
        }
    }
    else
    {
        //超时,未知状态
        disconnect(reply, SIGNAL(finished()), &eventloop, SLOT(quit()));
    }

    reply->abort();
    reply->deleteLater();
    //naManager->deleteLater();

    return -1;

注意:static QNetworkAccessManager* naManager 这个类型定义为静态变量,避免反复申请,一定要避免反复申请QNetworkAccessManager。

第二点,参考网上的资料,调用间隔最好大于100毫秒,而我的需求是间隔120ms,所以测试也没问题。

第三点,将控制权交给Application,使得事件完成,这样可以更好地释放QNetworkAccessManager和QNetworkReply等变量,前提是调用了成员函数deleteLater();

看看我的调用代码,使用了QCoreApplication::processEvents():

                 //注意需要避免内存泄露
                 ClientProgress* pCP = new ClientProgress;
                 int nCurProgress = pCP->CheckClientProgress(ci.strIp, ci.strProgressTxtPath, strInfo);
                 delete  pCP;

                 //防止内存泄露
                 QCoreApplication::processEvents();

个人感觉问题应该没有从根本上解决,Qt官方应该给出一个一劳永逸的解决办法才好,但是可能存在不少困难吧,个人猜测。

===========2021.04.01补充============

经过测试,问题依然没有彻底解决,泄露的情况依然存在,我尝试了各种办法,仍然无效,只能缓解,下面给出完整的类代码和调用代码。

这里之所以封装为类,是寄希望于手动控制类的生命周期以控制内存泄露,然而无效。

#include "ClientProgress.h"
#include <QNetworkAccessManager>
#include <QEventLoop>
#include <QTimer>

#include "taskFuncList.h"
#include "JsonParser.h"

ClientProgress::ClientProgress(QObject *parent) : QObject(parent)
{
    //避免反复申请
    m_naManager = new QNetworkAccessManager(this);
    m_naManager->setAutoDeleteReplies(true);
}

ClientProgress::~ClientProgress()
{
    if(m_naManager)
    {
        m_naManager->deleteLater();
    }
}

int ClientProgress::CheckClientProgress(std::string strHost, std::string strProgressFilePathName, std::string &strInfo)
{
    QStringList lKeys, lValues;
    lKeys.push_back("ip");
    lKeys.push_back("fun");
    lKeys.push_back("file");
    lValues.push_back(strHost.c_str());
    lValues.push_back(QString::number(enProgress));
    lValues.push_back(strProgressFilePathName.c_str());
    QByteArray baPostData = JsonParser::StringList2ByteArray(lKeys, lValues);

    QByteArray baReply;
    if (requestClientUrl(strHost.c_str(), baPostData, baReply, 2) == 0)
    {
        QString strProgress;
        if (JsonParser::ParseRootKeyValue(baReply, "progress", strProgress) == 0)
        {
            QStringList lProgress =  strProgress.split(",");

            if (lProgress.size() >= 2)
            {
                int nProgress = lProgress[0].toInt();
                if (nProgress >= 0 && nProgress <= 100)
                {
                    strInfo = lProgress[1].toUtf8().data();
                    return nProgress;
                }
            }
        }
    }

    return 0;
}

int ClientProgress::parseReplyData(QNetworkReply *reply, QByteArray &data)
{
    if (reply == nullptr)
    {
        return -1;
    }

    if(reply->error() != QNetworkReply::NoError)
    {
        return -2;
    }
    else
    {
        //输出内容
        data = reply->readAll();
    }

    return 0;
}

int ClientProgress::requestClientUrl(const QString &strHost, const QByteArray &baPostData, QByteArray &baAnswer, int nTimeoutSeconds)
{
    QString strCmdUrl = QString("http://") + strHost + ":" + QString::number(9998);

    QNetworkRequest request(strCmdUrl);
    setRequestHeader(request);

    QNetworkReply* reply = m_naManager->post(request, baPostData);
    QEventLoop eventloop;
    QMetaObject::Connection conRet = QObject::connect(reply, SIGNAL(finished()), &eventloop, SLOT(quit()));
    Q_ASSERT(conRet);

    QTimer timer;
    timer.singleShot(nTimeoutSeconds * 1000, &eventloop, SLOT(quit()));
    timer.start();

    eventloop.exec(QEventLoop::ExcludeUserInputEvents);

    if (timer.isActive())
    {
        QByteArray baReply;
        if (parseReplyData(reply, baReply) == 0)
        {
            reply->abort();
            reply->close();
            reply->deleteLater();
            //naManager->deleteLater();
            baAnswer = baReply;
            return 0;
        }
    }
    else
    {
        //超时,未知状态
        disconnect(reply, SIGNAL(finished()), &eventloop, SLOT(quit()));
    }

    reply->abort();
    reply->close();
    reply->deleteLater();
    //naManager->deleteLater();

    return -1;
}

没有使用对象数,因为是主线程,调用代码:

                 //注意需要避免内存泄露
                 ClientProgress* pCP = new ClientProgress;
                 int nCurProgress = pCP->CheckClientProgress(ci.strIp, ci.strProgressTxtPath, strInfo);
                 //pCP->deleteLater(); //此时new的时候加上this
                 delete pCP;
                 pCP = nullptr;

                 //防止内存泄露
                 QCoreApplication::processEvents();

此方法依然无法彻底解决问题,欢迎交流与讨论。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: Qt是一种跨平台的应用程序开发框架,可以用于实现不同操作系统上的图形用户界面、网络通信等功能。要使用Qt来实现SFTP上传文件,可以借助libssh库。 首先,我们需要在Qt项目中引入libssh库。可以通过在.pro文件中添加相应的配置信息,或者使用Qt的包管理器添加libssh库。 在代码中,我们首先需要建立一个SSH会话,使用libssh中的ssh_new函数来创建。然后,我们需要通过ssh_options_set函数设置SSH选项,包括远程主机的地址、端口号、用户名和密码等信息。 接下来,我们可以通过调用ssh_connect函数来建立SSH连接。如果连接成功,我们可以通过调用sftp_new函数创建一个SFTP会话。 在SFTP会话中,我们可以使用sftp_mkdir函数来创建远程目录,sftp_open函数来打开远程文件,sftp_write函数来向远程文件写入数据。 最后,我们使用sftp_close函数来关闭远程文件,sftp_free函数来释放SFTP会话,ssh_disconnect函数来断开SSH连接,以及ssh_free函数来释放SSH会话。 需要注意的是,在使用libssh进行SFTP上传文件时,我们还需要处理相关的错误和异常情况。可以使用libssh中提供的错误处理函数或者try-catch语句来捕获和处理异常。 总之,通过引入libssh库并使用其提供的函数,我们可以在Qt中实现SFTP上传文件的功能。这样,我们就可以轻松地在Qt项目中实现文件上传功能,提高应用程序的灵活性和功能性。 ### 回答2: Qt是一种跨平台的开发框架,其中也包含了用于网络操作的类和函数。要使用Qt进行SFTP上传文件,可以通过以下步骤: 首先,需要使用Qt的网络模块,导入相关头文件: ```cpp #include <QFile> #include <QDataStream> #include <QNetworkAccessManager> #include <QNetworkReply> #include <QSslConfiguration> ``` 然后,创建一个上传文件的函数,该函数接受文件路径、SFTP服务器地址、用户名和密码作为参数: ```cpp void uploadFile(const QString& filePath, const QString& serverAddress, const QString& username, const QString& password) { QNetworkAccessManager manager; // 设置SFTP连接的用户名和密码 manager.setCredentials(username, password); // 配置SSL QSslConfiguration sslConfig = QSslConfiguration::defaultConfiguration(); sslConfig.setProtocol(QSsl::TlsV1_2OrLater); manager.setSslConfiguration(sslConfig); QNetworkReply* reply = manager.put(QNetworkRequest(QUrl(serverAddress)), QFile(filePath)); reply->setParent(this); // 设置parent对象以避免内存泄漏 // 处理上传进度 connect(reply, &QNetworkReply::uploadProgress, [=](qint64 bytesSent, qint64 bytesTotal) { qDebug() << "Upload Progress:" << bytesSent << "/" << bytesTotal; }); // 处理上传完成 connect(reply, &QNetworkReply::finished, [=]() { if (reply->error() == QNetworkReply::NoError) { qDebug() << "File uploaded successfully!"; } else { qDebug() << "Error uploading file:" << reply->errorString(); } reply->deleteLater(); // 删除reply对象以释放资源 }); } ``` 最后,通过调用`uploadFile`函数即可完成文件的SFTP上传: ```cpp QString filePath = "path/to/local/file.txt"; QString serverAddress = "sftp://example.com/path/to/remote/file.txt"; QString username = "your_username"; QString password = "your_password"; uploadFile(filePath, serverAddress, username, password); ``` 以上就是使用Qt进行SFTP上传文件的简要说明。通过创建一个上传文件的函数,配置相应的SFTP连接参数,使用Qt的网络模块进行上传操作,即可完成文件的SFTP上传。 ### 回答3: 在Qt使用SFTP(Secure File Transfer Protocol)来上传文件,可以通过使用相应的Qt库和类来实现。 首先,需要引入Qt网络模块以及SFTP操作所需的类。在Qt头文件中引入如下代码: #include <QCoreApplication> #include <QNetworkAccessManager> #include <QNetworkReply> #include <QSshSocket> 然后,创建一个SFTP上传的函数,如下所示: void uploadFileToSftp(QString filePath, QString sftpServer, QString username, QString password, QString remotePath) { QSshSocket *ssh = new QSshSocket(); // 连接到SFTP服务器 ssh->connectToHost(sftpServer); ssh->login(username, password); // 上传文件 ssh->sftpConnect(); ssh->sftpCd(remotePath); ssh->sftpPut(filePath, remotePath); // 断开连接 ssh->sftpDisconnect(); ssh->disconnectFromHost(); } 其中,filePath是待上传的文件路径,sftpServer是SFTP服务器地址,username和password是登录SFTP服务器所需的用户名和密码,remotePath是远程目标路径。 最后,在主函数中调用上传函数,如下所示: int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QString filePath = "C:/path/to/file.txt"; QString sftpServer = "example.com"; QString username = "your_username"; QString password = "your_password"; QString remotePath = "/path/to/remote/folder"; uploadFileToSftp(filePath, sftpServer, username, password, remotePath); return a.exec(); } 这样,当运行程序时,会将指定的文件上传到SFTP服务器上的指定目录中。 需要注意的是,以上代码仅展示了基本的SFTP上传文件过程,实际应用中可能还需要处理一些异常情况以及上传进度等相关逻辑。同时,为了确保程序的安全性,建议对密码等敏感信息进行加密处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值