• 4155阅读
  • 10回复

[提问]QNetworkReply 崩溃,非法访问 [复制链接]

上一主题 下一主题
离线介农酥
 

只看楼主 倒序阅读 楼主  发表于: 2019-12-25
回复本帖可获得10RMB金钱奖励!
每人最多可获奖2次,奖池剩余10RMB金钱 (中奖几率80%)
我的程序一到中午就崩溃,我发现内存在不断的加,后来知道是因为QNetworkAccessManager管理的QNetworkReply内存需要释放,我做了试验,在finished的信号deleterLater,然后置nullptr,确实可以防止内存泄漏,但是我不知道为什么,我在finished信号槽中访问reply会崩溃,finished槽中reply难道还会NULL。我只能在finished信号对应的槽中加一个判断
    if (!reply) {
        return;
    }

但是我网上和官网的看的几个例子,好像没有先判断这个reply是不是非法。如果非法,我之前没有deleterLater和置nullptr的时候,怎不见得崩溃。

程序说明如下,这个QNetworkAccessManager在异步请求的时候,还会有其他请求,所以,本次finished响应不一定是本次请求发出的,可能是它后一个请求。关键代码如下:
    reply = manager->get(request);
    connect(reply, SIGNAL(finished()), this, SLOT(httpRead()));

void ZT::httpRead()
{
//非要加个reply是否空的判断吗?
    if (!reply) {
        return;
    }
    if (reply->error()) {
        //错误直接返回,因为连条码都没有
        qDebug() << "中通返回错误" << reply->errorString();
        reply->deleteLater();
        reply = nullptr;
        return;
    }
......
    reply->deleteLater();
    reply = nullptr;
}


目前崩溃是不是因为内存泄漏,还不清楚,只能等这个好了再说,还有这个finished信号和readyReady哪个信号作为读取的比较好,我之前一个程序返回的数据比较多,在readyReady的信号槽中读取,会读取不完成,但是finished能够全部读取,所以我后面的程序都改成finished的了。

2019/12/26追加

我发现有这么一个现象,关键代码如下:

request.setUrl(QUrl(postUrl));reply = manager->get(request);
//直接deleteLater
reply->deleteLater();reply = nullptr;

上面的代码不会崩溃,但好像没有这么做的。
---------------------------------------------------------------------------------
request.setUrl(QUrl(postUrl));
reply = manager->get(request);
connect(reply, &QNetworkReply::finished, this, [=]() //在QNetWorkReply的finished信号中deleteLater
{
        if (reply->error()) {
            //错误直接返回,因为连条码都没有
            qDebug() << "回传返回错误" << reply->errorString();
            reply->deleteLater();
            reply = nullptr;
            return;
}

        reply->deleteLater();
        reply = nullptr;
});

上面的代码会很快崩溃。这点其实不是很理解,因为我程序的另外一个类中的http请求在finished中槽deleteLater,虽然也会崩溃,但不会这么快。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

request.setUrl(QUrl(postUrl));reply = manager->get(request);
connect(manager, &QNetworkReply::finished, this, [=](QNetworkReply *reply)   //改成manager的finished信号,然后使用传过来的reply{
        qDebug() << reply;        reply->deleteLater();
        reply = nullptr;});
-------------------------------------------------------------------------------------这个最奇诡
它不会崩溃(当然我也只是运行半个小时左右吧,长了不知道会不会崩),但是我发现每次执行的时候qDebug() << reply; 会执行很多次,打印如下

QNetworkReplyHttpImpl(0x8ce5450)QNetworkReplyHttpImpl(0x8ce5450)
QNetworkReplyHttpImpl(0x8ce5450)QNetworkReplyHttpImpl(0x8ce5450)

前面一两次一条没有,但是后面的越来越多,一次执行打印出的地址都是一样的,但是每次执行的地址又是不一样的。显然,感觉监听这个也不靠谱。
离线20091001753

只看该作者 1楼 发表于: 2019-12-25
回帖奖励+ 10
  1. auto reply = manager.get(QNetworkRequest(QUrl("http://www.qtcn.org/")));
  2. connect(reply,&QNetworkReply::finished,this,[&,reply]{
  3.     if(!reply->error()){
  4.         qDebug()<<reply->readAll().size();
  5.     }else qDebug()<<reply->errorString();
  6.     reply->deleteLater();
  7. });

(づ ̄ 3 ̄)づ
离线介农酥

只看该作者 2楼 发表于: 2019-12-25
回 20091001753 的帖子
20091001753:auto reply = manager.get(QNetworkRequest(QUrl("http://www.qtcn.org/")));
connect(reply,&QNetworkReply::finished,this,[&,reply]{
    if(!reply->error()){
        qDebug()<<re .. (2019-12-25 17:24)

谢谢回复,恕我直言,没感觉有什么不同啊,你这里唯一就是没有reply=NULL,reply是成员变量,匿名函数方括号我按照你的写法,提示错误,但是这和发出信号的reply应该是一样的吧。其实我看网上确实很多没有置NULL的,我是看到官网有一个例子置null,所以我也置null了,但置null为什么就非法访问,我就不明白了。还请赐教!

离线angelus

只看该作者 3楼 发表于: 2019-12-26
回 介农酥 的帖子
回帖奖励+ 10
介农酥:谢谢回复,恕我直言,没感觉有什么不同啊,你这里唯一就是没有reply=NULL,reply是成员变量,匿名函数方括号我按照你的写法,提示错误,但是这和发出信号的reply应该是一样的吧。其实我看网上确实很多没有置NULL的,我是看到官网有一个例子置null,所以我也置null了,但置null为 .. (2019-12-25 21:09)

reply = manager.get(QNetworkRequest(QUrl("http://www.qtcn.org/")));
如果你是复用这行代码,有可能有多个地点调用这个函数,不要把reply定义成成员变量
尽量在使用的时候创建
QNetworkReply *reply =  manager.get(QNetworkRequest(QUrl("http://www.qtcn.org/")));

然后在对应的槽里边获取QNetworkReply *reply =  qobject_cast<QNetworkReply*>(sender());
取出数据,然后删除该指针,释放内存。如果你想加上双保险,防止内存泄漏,就创建一个列表,
创建一个指针就把指针加入到列表中,在槽中如果释放就删除列表中对应的,然后创建一个界面,监控这个列表就行

如果你使用的是同步调用,那么用成员变量是没问题的,用完释放指针然后置空
本身多次异步调用,还用的同一个指针,会有未知的风险的


离线介农酥

只看该作者 4楼 发表于: 2019-12-26
回 angelus 的帖子
angelus:
reply = manager.get(QNetworkRequest(QUrl("http://www.qtcn.org/")));
如果你是复用这行代码,有可能有多个地点调用这个函数,不要把reply定义成成员变量
尽量在使用的时候创建
QNetworkReply *reply =  manager.get(QNetworkRequest(QUrl("http://www.qtcn.org/")));
.......

谢谢回复!我试试!

我发现有这么一个现象,关键代码如下:

request.setUrl(QUrl(postUrl));
reply = manager->get(request);
//直接deleteLater

reply->deleteLater();reply = nullptr;

上面的代码不会崩溃,但好像没有这么做的。
---------------------------------------------------------------------------------
request.setUrl(QUrl(postUrl));
reply = manager->get(request);
connect(reply, &QNetworkReply::finished, this, [=]() //在QNetWorkReply的finished信号中deleteLater
{
        if (reply->error()) {
            //错误直接返回,因为连条码都没有
            qDebug() << "回传返回错误" << reply->errorString();
            reply->deleteLater();
            reply = nullptr;
            return;
}

        reply->deleteLater();
        reply = nullptr;
});

上面的代码会很快崩溃。这点其实不是很理解,因为我程序的另外一个类中的http请求在finished中槽deleteLater,虽然也会崩溃,但不会这么快。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

request.setUrl(QUrl(postUrl));
reply = manager->get(request);
connect(manager, &QNetworkReply::finished, this, [=](QNetworkReply *reply)   //改成manager的finished信号,然后使用传过来的reply
{
        qDebug() << reply;
        reply->deleteLater();
        reply = nullptr;
});
-------------------------------------------------------------------------------------
这个最奇诡
它不会崩溃(当然我也只是运行半个小时左右吧,长了不知道会不会崩),但是我发现每次执行的时候qDebug() << reply; 会执行很多次,打印如下

QNetworkReplyHttpImpl(0x8ce5450)
QNetworkReplyHttpImpl(0x8ce5450)
QNetworkReplyHttpImpl(0x8ce5450)
QNetworkReplyHttpImpl(0x8ce5450)


前面一两次一条没有,但是后面的越来越多,一次执行打印出的地址都是一样的,但是每次执行的地址又是不一样的。显然,感觉监听这个也不靠谱。
离线angelus

只看该作者 5楼 发表于: 2019-12-26
回帖奖励+ 10
未知错误很可能是比较诡异的,你先修改成非成员变量形式,如果还是报错,记得把代码发上来,我试试看
离线20091001753

只看该作者 6楼 发表于: 2019-12-26
在线翻译
http://www.qtcn.org/bbs/read-htm-tid-87901.html

这个 Demo 供你参考。
它的运作,就是通过 QNetworkAccessManager::get 访问百度翻译API
(づ ̄ 3 ̄)づ
离线flyer_2001

只看该作者 7楼 发表于: 2019-12-27
m_pNetAccManager = new QNetworkAccessManager(this);
connect(m_pNetAccManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(finishedSlot(QNetworkReply*)));

void finishedSlot(QNetworkReply *reply){
    reply->deleteLater();
}
离线feng851

只看该作者 8楼 发表于: 2019-12-27
不清楚你代码具体内容是什么,但我相信肯定reply 在slot函数中使用时就已经发生了变化,甚至已经delete了,建议你好好跟跟reply相关代码
离线介农酥

只看该作者 9楼 发表于: 2019-12-29
回 20091001753 的帖子
20091001753:在线翻译
http://www.qtcn.org/bbs/read-htm-tid-87901.html
这个 Demo 供你参考。
它的运作,就是通过 QNetworkAccessManager::get 访问百度翻译API (2019-12-26 18:59) 

您好,为什么我点击下载,没什么反应,下载不了。
离线玖零儛

只看该作者 10楼 发表于: 2020-01-02
这个问题我也碰到过,是在访问一个不可达的网址的时候,我直接关闭客户端,析构直接删了QNetworkAccessManager,QNetworkReply也被连带删除,这时候其他地方再访问QNetworkReply指针就直接崩溃了。我的处理方法是连接QNetworkReply的destroyed信号,被销毁了就把相关指针置为nullptr。你的情况可能也是Qt自己把这对象删了,你在外面又持有这个对象的指针。
快速回复
限100 字节
 
上一个 下一个