Crash in QNetworkManager when used in secondary thread [SOLVED]



  • I have been working on a download manager. The application uses Qthreads, and QNetworkManager object is created in the run(). The downloads starts but the application crashes in between with the error "The program has unexpectedly finished." The debugger stops at
    "file <unavailable synchronous data>" where file is an QFile object.
    After trying hard to find out the error, i concluded that
    file.write(reply->read(read->bytesAvailable()) causes the application to crash.( reply is an QNetworkReply object)
    if this line is commented every thing works fine, so plz help me out. Below is a part of source code downloadThread.cpp

    @
    #include "downloadthread.h"
    #include "downax.h"
    #include <QMessageBox>
    #include <Qt/qtest.h>

    DownloadThread::DownloadThread(QObject *parent) :
    QThread(parent)
    {

    }

    void DownloadThread::setUrl(QString surl)
    {
    final_url=surl;
    }

    void DownloadThread::run()
    {
    QString args =final_url;
    if (args.isEmpty())
    {
    emit invalidUrl_t();
    return;
    }
    QNetworkProxy proxy;
    QNetworkAccessManager manager1;
    QUrl url = QUrl::fromEncoded(args.toLocal8Bit());
    proxy.setType(QNetworkProxy::HttpProxy);
    proxy.setHostName("172.16.12.2");
    proxy.setPort(3128);
    manager1.setProxy(proxy);

    QNetworkRequest request(url);//request.
    reply=manager1.get(request);
    downloadTime.start();
    connect(&manager1, SIGNAL(finished(QNetworkReply*)),
            SLOT(downloadFinished(QNetworkReply*)));
    connect(reply, SIGNAL(readyRead()),
            SLOT(saveToDisk()));
    connect(reply, SIGNAL(downloadProgress(qint64,qint64)),
            SLOT(showProgress_t(qint64,qint64)));
    QUrl purl = reply->url();
    QString filename = saveFileName(purl);
    file.setFileName(filename);
    if (!file.open(QIODevice::WriteOnly))
    {
       // fprintf(stderr, "Could not open %s for writing: %s\n",
                //qPrintable(filename),
               // qPrintable(file.errorString()));
        //return false;
    }
    
    
    exec&#40;&#41;;
    

    }

    void DownloadThread::showProgress_t(qint64 curr,qint64 total)
    {
    QString unit;
    float curr_r,total_r,curr_s;
    curr_r=curr_s=curr;total_r=total;
    curr_r=curr_r/(10241024);total_r=total/(10241024);
    float speed = curr_s * 1024.0 / downloadTime.elapsed();
    if (speed < 1024) {
    unit = "bytes/sec";
    } else if (speed < 10241024) {
    speed /= 1024;
    unit = "kB/s";
    } else {
    speed /= 1024
    1024;
    unit = "MB/s";
    }
    curr_r = floorf(curr_r * 100 + 0.5) / 100;
    speed = floorf(speed * 100 + 0.5) / 100;

        QString progress=QString::number(curr_r)+" MB  "+QString::number(speed)+" "+unit+"   "+QString::number(total_r)+"MB";
    emit
            downloadProgress_t(progress);
    

    }

    QString DownloadThread::saveFileName(const QUrl &url)
    {

     QString path = url.path();
     QString basename = QFileInfo(path).fileName();
    
     if (basename.isEmpty())
         basename = "download";
    
     if (QFile::exists(basename)) {
         // already exists, don't overwrite
         int i = 0;
         basename += '.';
         while (QFile::exists(basename + QString::number(i)))
             ++i;
    
         basename += QString::number(i);
     }
    
     return basename;
    

    }

    void DownloadThread::saveToDisk()
    {

     qint64 bytes;
     //QByteArray temp;
     bytes=reply->bytesAvailable();
    
     file.write(reply->read(bytes));
    

    }

    void DownloadThread::downloadFinished(QNetworkReply* replyy)
    {
    QString fileUrl;
    QUrl url = reply->url();
    fileUrl=url.toEncoded().constData();
    if (reply->error())
    {
    emit errorDownloading_t(fileUrl,reply->errorString());
    }
    else
    {
    QString filename = saveFileName(url);
    // if (saveToDisk(filename, reply))
    // emit downloadComplete_t(fileUrl,filename);
    file.close();
    }

     //reply->deleteLater();
     this->exit();
    

    }
    @

    [Edit] please use code wrappers



  • First: your QFile object (lacking additional information, I assume it is declared in the class header as attribute) is created in the main thread, as is the QThread subclass itself. You're doing it wrong.™ - see "Threads, Events and QObjects":/wiki/Threads_Events_QObjects wiki article for details.

    Second: why do you use a thread at all? QNetworkAccessManager is asynchronous by default (and uses internal threads from Qt 4.8 on). There is usually no need to put it into a thread of its own.

    Third: simplify the saveToDisk method:

    @
    void DownloadThread::saveToDisk()
    {
    file.write(reply->readAll());
    }
    @

    To analyze further, it would be useful to know at which line the error occurs. Otherwise, a small, complete, compilable example is useful. Leave out everything that is not needed to reproduce the error (e.g. the showProgress_t slot, etc.). Just the class causing the trouble and a main function.

    But please consider seriously dropping your threading, this just adds double asynchronity.

    PS:
    If the error is on file, why do you suspect QNetworkAccessManger to crash, as stated in the thread's title?



  • Sir, thank you for replying, actually i am developing a download manager, and to have many simultaneous downloads I opted for threads. Besides, i am going to develop it into a full fledged gui download manager so i think using multithreading is important. If i replace file.write(reply->readAll())
    by QByteArray data=reply->replyAll() with no file object, again the same error occurs. The problem seems to be with QNetworkReply object even in simpler code (multithreaded).
    Besides if i connect finished() signal of networkaccessmanager object to file.write(reply->readAll()), things work fine but behaviour for multiple downloads is wierd. Sometimes the application crashes after two simultaneous downloads or three etc.
    compilable Code:
    http://sharescanner.net84.net/DownAx.tar.gz



  • oh thanks, it worked like a charm, there was no need of using threads



  • Sir, is it possible to create a multi-segment downloader using QNetwork,



  • What's a "multi-segment downloader"?



  • Sir, suppose i wish to download a file. so i will simply do:
    QNetworkManager manager.get(request(url))

    Both HTTP and FTP protocols allow the client to specify the start position of the stream. So can QNetworkRequest object be made to generate such a request where we can specify the starting position of the the stream, and if we can, then we can use manager.get(request(url)) multiple times (perhaps upto 6 times as mentioned in qt-doc) to download the the same file at different positions thereby accelerating the download.



  • Do people still actually use such crappy software such as download "managers" or "accelerators". First of all, I think it is anti-social behaviour to use them to accelerate your downloads. If the server is imposing a limit on the bandwidth to be allocated for a single connection, it does so for a reason. Why do you think you deserve more bandwidth than anyone else?
    Using them to resume your downloads because you are on an unstable connection is a different discussion.

    Anyway, AFAIK, this part of the HTTP protocol is not supported by QNAM.



  • You can always send your custom headers with the QNetworkRequst. It should be possible to indicate a starting position for a transfer. It's up to you to assemble the different bits and bytes in the correct order to the final file, though.



  • Sir, can you plz help me with creating such type of a header. A simple illustrative example would be highly appreciated.



  • "QNetworkRequest::setRawHeader() ":/doc/qt-4.8/qnetworkrequest.html#setRawHeader should get you further.



  • Thanks Sir, it worked but still a problem.
    Suppose the file size is 1256498 bytes

    request.setRawHeader("Range: bytes=0-1256498",""); //downloads the whole file as expected

    request.setRawHeader("Range: bytes=0-12564",""); //downloads 12565 bytes as expected

    request.setRawHeader("Range: bytes=5-13339077",""); //downloads 394 bytes for any byte range not starting with zero



  • If you do that on the very same url, how should that work?

    If the file has a size of 1256498, as you state in in the first version "downloads the whole file", then a range of 13339077 clearly exceeds the limits (it's more than 10 times bigger).

    Did you look at the 394 bytes in the third answer? Could be that it contains an error message?

    BTW: The correct usage of setRawHeader is:

    @
    req.setRawHeader("Range", "bytes=12-155");
    @



  • Sir, actually i had made a mistake in the question in the third case, it was obviously less than the file size.
    And the problem was with the syntax as you stated and it is now working, even on the same url. Thanks again and thanks again for always replying to my queries.



  • My downloader is working fine and i am testing it right now on many sites. Surprisingly it couldn't download any file from furk.net.
    I tried a simple program using networkaccessmanager it still failed i.e. no problem with my downloader. I tried downloading using other downloaders:
    fatrat: also developed using qt (it also failed)
    uget: successfully started download
    firefox in-built: successful
    I doubt there is some problem in parsing the url. I am pasting the url but it is expired:
    http://g9uau0ao42n1kortjknotrql05sr45pbh4uq8s8.gcdn.biz/d/r/Y4APk1wSRw6QCiOdWl1tKD-hKUKyDHCYn5rsDoRf9gPxJkYidwuSQSDO6xBEBEwDZ0ZGZl0iWW384kTVqcNOBw/black Stone V3 - 6.zip

    there are no spaces at end actually "percent20" which were replaced by spaces by this editor
    Perhaps such long urls or "percent20" are not supported by qurl/qstring. plz help



  • Anybody plz reply


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.