2016-11-19 91 views
0

****更新:我注意到,我只在Windows上得到segfault,在Linux上它沒有問題。在Windows上,我使用QT 5.5和MinGW32。我仍然想知道爲什麼。使用QNetworkAccessManager-> Post()會導致SEGV關閉應用程序

****初始問題: 這裏沒什麼棘手,我創建了一個基本的控制檯應用程序。我有一個QNetworkAccessManager發送Post()請求。當我關閉控制檯時,會出現段錯誤。

請注意,請求發送和接收成功,我的問題只是關於該段錯誤。

如果未發送Post()請求,則在關閉控制檯時不會發生崩潰。堆棧沒有太多的幫助。

堆棧

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

您可以從'onFinished()'發出一個信號並將其連接到應用程序的'quit()'插槽。轉移完成後,應該結束應用程序。目前關閉應用程序可能會觸發一種導致崩潰的終止形式 –

+0

我注意到,我只在Windows上獲得了segfault,在Linux上沒有問題。在Windows上,我使用QT 5.5和MinGW32。我仍然想知道爲什麼。 – vincedjango

+1

基本代碼可能無法處理信號,或者Windows正在使用終端關閉時終止程序。在Linux上,這可能是一個標準信號,並導致應用程序正常終止,而在Windows上它可能不會。在搜索窗口的信號終端關閉方式時發現這一點:http://stackoverflow.com/questions/20511182/catch-windows-terminal-closing-on-running-process –

回答

1

當您關閉控制檯時,您的程序將以最不正常的方式死亡。你需要編寫一些代碼,使其優雅而不是: 下面的是一個完整的測試用例:

// 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(); 
} 

如果按Ctrl-C或單擊[x]在控制檯窗口,關機是有序的,輸出是:

[...] 
bye world 
reply gone 
manager gone