0

**** MISE À JOUR: J'ai remarqué que je ne reçois la segfault que sous Windows, sous Linux, ça va. Sous Windows, j'utilise QT 5.5 et MinGW32. Je veux toujours savoir pourquoi.L'utilisation de QNetworkAccessManager-> Post() provoque SEGV à la fermeture de l'application

**** Question initiale: Rien de compliqué ici, je crée une application de console de base. J'ai un QNetworkAccessManager envoyant une demande Post(). Quand je ferme la console, il y a un segfault.

Notez que la demande est envoyée et reçue avec succès, ma question concerne uniquement cette erreur de segmentation.

Si aucune requête Post() n'est envoyée, aucun blocage ne se produit lors de la fermeture de la console. Il n'y a pas beaucoup d'aide de la pile.

Stack

0 ntdll!RtlFreeHeap   0x77b5e041 
1 ucrtbase!free   0x5e4c5eab 
2 LIBEAY32!CRYPTO_free   0x5e5a123e 

Main.cpp

#include <QCoreApplication> 
#include "CNetworkHandleTest.h" 

int main(int argc, char *argv[]) 
{ 
    QCoreApplication a(argc, argv); 

    CNetworkHandleTest net; 
    net.start(); 

    return a.exec(); 
} 

CNetworkHandleTest.cpp

#include "CNetworkHandleTest.h" 

CNetworkHandleTest::CNetworkHandleTest() 
{ 
    m_Manager = new QNetworkAccessManager(this); 
    // Connect the network manager so we can handle the reply 
    connect(m_Manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(onFinished(QNetworkReply*))); 
    m_nTotalBytes = 0; 
} 

CNetworkHandleTest::~CNetworkHandleTest() 
{ 
    disconnect(); 
    m_Manager->deleteLater(); 
} 

void CNetworkHandleTest::onFinished(QNetworkReply* reply) 
{ 
    // Look at reply error 
    // Called when all the data is receivedqDebug() << "Error code:" << reply->error(); 
    qDebug() << "Error string:" << reply->errorString(); 
    reply->close(); 
    reply->deleteLater(); 
} 

void CNetworkHandleTest::start() 
{ 
    // Configure the URL string and then set the URL 
    QString sUrl(BASE_URL); 
    sUrl.append("/console/5555/upload"); 
    m_Url.setUrl(sUrl); 

    // Make the request object based on our URL 
    QNetworkRequest request(m_Url); 

    // Set request header (not sure how or why this works, but it works) 
    // \todo investigate 
    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlenconded"); 

    // Make file object associated with our DB file 
    QFile file("/tx_database.db"); 
    if(!file.open(QIODevice::ReadOnly)) 
    { 
     qDebug() << "Failed to open file"; 
    } 

    // Read the entire file as a binary blob 
    QByteArray data(file.readAll()); 

    // Set our request to our request object 
    // Note: there should probably be a flag so that when start is called it does not do 
    // any processing in case we are already in the middle of processing a request 
    m_Request = request; 

    // Send it 
    m_Reply = m_Manager->post(m_Request, data); 

    // Need to connect the signals and slots to the new reply object (manager makes a new 
    // reply object every post; may need to investigate if memory should be freed when 
    // done processing a response) 
    connect(m_Reply, SIGNAL(readyRead()), this, SLOT(onReadyRead())); 
    connect(m_Manager, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)), 
      this,  SLOT(onAuthenticationRequired(QNetworkReply*, QAuthenticator*))); 
} 

void CNetworkHandleTest::onReadyRead() 
{ 
    // Whenever data becomes available, this slot is called. It is called every time data 
    // is available, not when all the data has been received. It is our responsibility to 
    // keep track of how much we have received if we want to show progress or whatever 
    // but we do not need to keep track if we have received all the data. The slot 
    // OnFinished will be called when the all the data has been received. 

    qDebug() << "Bytes available:" << m_Reply->bytesAvailable(); 
    m_nTotalBytes += m_Reply->bytesAvailable(); 
    qDebug() << "Bytes thus far:" << m_nTotalBytes; 
    QByteArray responseData = m_Reply->readAll(); 
    qDebug() << "Response" << responseData; 
    m_Reply->size(); 
} 

void CNetworkHandleTest::onAuthenticationRequired(QNetworkReply* reply, QAuthenticator* authenticator) 
{ 

} 

CNetworkHandleTest.h

#ifndef CNETWORKHANDLETEST_H 
#define CNETWORKHANDLETEST_H 

// Required packages 
#include <QNetworkAccessManager> 
#include <QNetworkRequest> 
#include <QNetworkReply> 
#include <QByteArray> 
#include <QUrlQuery> 
#include <QHostInfo> 
#include <QObject> 
#include <QUrl> 

// Helper packages 
#include <QCoreApplication> 
#include <QFile> 
#include <QDir> 

// Our packages 
#include <iostream> 
#include <fstream> 
#include <cstdlib> 

#define BASE_URL "localhost:5000" 
#define BOUNDARY "123456787654321" 

class CNetworkHandleTest : public QObject 
{ 
    Q_OBJECT 

public: 
    CNetworkHandleTest(); 
    ~CNetworkHandleTest(); 

    void start(); 

protected Q_SLOTS: 

    void onFinished(QNetworkReply* reply); 
    void onReadyRead(); 
    void onAuthenticationRequired(QNetworkReply* reply, QAuthenticator* authenticator); 

private: 

    QNetworkAccessManager* m_Manager; 
    QNetworkRequest  m_Request; 
    QNetworkReply*  m_Reply; 
    QUrl     m_Url; 

    int    m_nTotalBytes; 
}; 

#endif // CNETWORKHANDLETEST_H 
+0

Vous pouvez émettre un signal à partir de 'onFinished()' et le connecter à l'emplacement 'quit()' de l'application. Cela devrait mettre fin à la demande une fois le transfert terminé. Fermeture de l'application en cours pourrait déclencher une forme de résiliation qui entraîne l'accident –

+0

J'ai remarqué que je reçois le segfault uniquement sur Windows, sur Linux, ça va. Sous Windows, j'utilise QT 5.5 et MinGW32. Je veux toujours savoir pourquoi. – vincedjango

+1

Le code de base ne gère probablement pas le signal ou tout ce que Windows utilise pour terminer le programme lorsqu'un terminal est fermé. Sous Linux, il s'agira vraisemblablement d'un signal standard et provoquera la fin de l'application, alors que ce n'est pas le cas sur Windows. Trouvé ceci lors de la recherche de la manière de fermeture de terminal de signalisation de Windows: http://stackoverflow.com/questions/20511182/catch-windows-terminal-closing-on-running-process –

Répondre

1

Lorsque vous fermez la console, votre programme meurt de la manière la plus désagréable. Vous devez écrire un code pour le rendre grâce à la place: Le dessous est un test complet:

// https://github.com/KubaO/stackoverflown/tree/master/questions/network-cleanup-40695076 
#include <QtNetwork> 
#include <windows.h> 

extern "C" BOOL WINAPI handler(DWORD) 
{ 
    qDebug() << "bye world"; 
    qApp->quit(); 
    return TRUE; 
} 

int main(int argc, char *argv[]) 
{ 
    SetConsoleCtrlHandler(&handler, TRUE); 
    QCoreApplication a(argc, argv); 

    QNetworkAccessManager mgr; 
    int totalBytes = 0; 

    QObject::connect(&mgr, &QNetworkAccessManager::finished, [](QNetworkReply *reply){ 
     qDebug() << "Error string:" << reply->errorString(); 
    }); 

    QNetworkRequest request(QUrl{"http://www.google.com"}); 
    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlenconded"); 

    auto reply = mgr.post(request, QByteArray{"abcdefgh"}); 
    QObject::connect(reply, &QIODevice::readyRead, [&]{ 
     qDebug() << "Bytes available:" << reply->bytesAvailable(); 
     totalBytes += reply->bytesAvailable(); 
     qDebug() << "Bytes thus far:" << totalBytes; 
     reply->readAll(); 
    }); 
    QObject::connect(reply, &QObject::destroyed, []{ 
     qDebug() << "reply gone"; 
    }); 
    QObject::connect(&mgr, &QObject::destroyed, []{ 
     qDebug() << "manager gone"; 
    }); 
    return a.exec(); 
} 

Si vous appuyez sur Ctrl-C ou cliquez [x] sur la fenêtre de la console, l'arrêt est ordonnée, la sortie étant :

[...] 
bye world 
reply gone 
manager gone