2012-08-17 56 views
0

我正在處理我懷疑在使用QNetworkAccessManager期間發生的程序中發生的堆棧損壞錯誤。完整的源代碼可用here使用QNetworkAccessManager期間可能的堆棧損壞

從日誌輸出來判斷,我猜測這個bug是在calendar.cpp的第44行的異步請求之後觸發的。我沒有這方面的確鑿證據;我只注意到第45行的消息是大多數時間最後的日誌消息。

這裏是線39-46:

void Calendar::update() 
{ 
    // Start calendar download asynchronously. After retrieving the 
    // file, parseNetworkResponse will take over. 
    Logger::instance()->add(CLASSNAME, "Fetching update for 0x" + QString::number((unsigned)this, 16) + "..."); 
    _naMgr.get(QNetworkRequest(_url)); 
    Logger::instance()->add(CLASSNAME, "Update request for 0x" + QString::number((unsigned)this, 16) + " was filed."); 
} 

該HTTP GET請求由Calendar::parseNetworkResponse插槽,其被連接到_naMgrfinished信號處理。下面是線170-174:

void Calendar::parseNetworkResponse(QNetworkReply* reply) { 
    // TODO: we suspect that sometimes a SIGSEGV occurs within the bounds 
    // of this function. We'll remove the excessive log calls when we've 
    // successfully tracked down the problem. 
    Logger::instance()->add(CLASSNAME, "Got update response for 0x" + QString::number((unsigned)this, 16) + "."); 

即使上線45日誌消息不是最後出現在日誌崩潰後,總有在日誌中的更新請求是永遠不會跟進通過第174行的日誌消息。這使我相信,HTTP GET請求可能會破壞這裏的東西。 GET請求提交的URL似乎是正確的。

我認爲存在內存損壞的原因之一是,即使輸入日曆列表和我的Internet連接狀態保持不變,該錯誤並不會始終彈出。 (我可以用至少2個日曆觸發bug。)此外,當我嘗試瞭解更多關於失敗點的信息時,我看到了this GCC output。我會從valgrind的內存檢查器收集輸出以收集更多信息,但目前我附近沒有可用的Linux安裝。

這個bug可能與我身邊的Qt網絡庫的錯誤使用有關嗎?你有什麼其他的理論可能會導致這個問題?這是我第一次處理可能的堆棧損壞問題,因爲在業餘時間我正在做這個單獨的愛好項目,所以我沒有人來訓練我解決這些問題。

回答

0

在已完成的插槽,當你處理響應設置一個全局標誌(布爾)來「處理」

這樣一來,如果你的另一響應,您可以對其進行排隊或者你可以忽略它。問題可能更多的是由於QNetworkAccessManager派生一個線程在後臺某處執行處理的事實,這會導致您的代碼執行訪問衝突。

如果你想我會跳上你的項目,有一個仔細看看:)

〜丹

我的發送和接收類:

.h文件中

class sendRec : public QObject 
{ 
    Q_OBJECT 
public: 
    sendRec(QObject *parent = 0); 
    sendRec(QUrl); 
    QString lastError; 
    QUrl thisURL; 
    //QNetworkAccessManager manager; 
    //FUNCTIONS 
    void doGet(); 
    void doPost(QByteArray*); 
    void doPut(QString, QString); 
    void doConnects(QNetworkReply *reply, QNetworkAccessManager *manager); 

signals: 
    void sendResponse(bool, QString*); 
public slots: 

    /** NETWORK_ACCESS_MANAGER_SLOT **/ 
    //SUCCESS SLOTS FOR BOTH 
    void requestReturned(QNetworkReply * reply); 
    //FAILURE SLOTS 
    void proxyAuthFail(const QNetworkProxy & proxy, QAuthenticator * authenticator); 
    void sslErrorFail(QNetworkReply * reply, const QList<QSslError> & errors); 
    /** QNETWORK_REPLY_SLOTS **/ 
    //FAILURE SLOTS 
    void reqError (QNetworkReply::NetworkError code); 
    void sslError (const QList<QSslError> & errors); 

}; 

.cpp:

#include "sendrec.h" 

sendRec::sendRec(QObject *parent) : 
    QObject(parent) 
{ 
} 


sendRec::sendRec(QUrl url) { 
    thisURL = url; 
} 

void sendRec::doGet() { 
    QNetworkRequest request(thisURL); 
    QNetworkAccessManager *manager = new QNetworkAccessManager(this); 
    QNetworkReply *reply = manager->get(request); 
    doConnects(reply, manager); 
} 

void sendRec::doPost(QByteArray *message) { 
    QNetworkRequest request(thisURL); 
    QNetworkAccessManager *manager = new QNetworkAccessManager(this); 
    QNetworkReply *reply = manager->post(request, *message); 
    doConnects(reply, manager); 
} 

void sendRec::doConnects(QNetworkReply *reply, QNetworkAccessManager* manager){ 
    //Reply Connects 
    QObject::connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), 
        this, SLOT(reqError(QNetworkReply::NetworkError))); 
    QObject::connect(reply, SIGNAL(sslErrors(QList<QSslError>)), 
        this, SLOT(sslError(QList<QSslError>))); 
    //manager connects 
    QObject::connect(manager, SIGNAL(finished(QNetworkReply*)), 
       this, SLOT(requestReturned(QNetworkReply*))); 
    QObject::connect(manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)), 
      this, SLOT(sslErrorFail(QNetworkReply*,QList<QSslError>))); 
    QObject::connect(manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), 
       this, SLOT(proxyAuthFail(QNetworkProxy,QAuthenticator*))); 


} 


void sendRec::requestReturned(QNetworkReply * rep){ 
    qDebug() << "Request Returned"; 
    QVariant status = rep->attribute(QNetworkRequest::HttpStatusCodeAttribute); 
    if(status != 200 || status == NULL) { 
     QString *lastError = new QString("ERROR: " + status.toString() 
             + " " + rep->readAll()); 
     emit sendResponse(false, lastError); 
    } else { 
     QString *retString = new QString(rep->readAll()); 
     *retString = retString->trimmed(); 
     emit sendResponse(true, retString); 
    } 
    rep->manager()->deleteResource(rep->request()); 
    rep->manager()->deleteLater(); 
    rep->deleteLater(); 
    sender()->deleteLater(); 


} 
+0

我認爲'Calendar :: parseNetworkResponse'總是在接收線程上執行,因爲我的['Qt :: ConnectionType'](http://qt-project.org/doc/qt-4.8/qt.html#ConnectionType-枚舉)設置爲'Qt :: AutoConnection'。這是不是意味着'Calendar :: parseNetworkResponse'永遠不會同時在多個線程中執行?或者也許我不太瞭解'QNetworkAccessManager'的內在機制。 – Pieter 2012-08-19 10:40:25

+0

它背後有很多東西。我寫了一個使用它的非常複雜的程序。我在這裏從這個網站學到了很多:http://www.johanpaul.com/blog/2011/07/why-qnetworkaccessmanager-should-not-have-the-finishedqnetworkreply-signal/ – FaddishWorm 2012-08-19 10:42:58

+0

就像我之前說的那樣,它總是很好有一個這種東西的包裝類:) – FaddishWorm 2012-08-19 10:43:14

0

您是否一次發送兩個請求?

我有一個線程和QNetworkAccessManager的問題,我不得不爲它編寫一個包裝。

好的,我讀了一些你的代碼,除了完成的信號外,還有一些其他的信號會在失敗時發出 - 它總是很好的抓住這些信號。

在完成處理響應之前,是否有機會發送另一個獲取請求,這可以解釋爲什麼它是間歇性的。

我會繼續尋找,看看我能否提供幫助。

〜丹

+0

感謝您花時間分析我的代碼。當我在四處挖掘時,我還發現一個空指針錯誤(* calendar.cpp:207 *),當您添加一些不是有效的ICS文件時會發生這種錯誤。 *「您是否一次發送兩個請求?」*您好,您提到...我可能必須在'Calendar :: update'中使用互斥鎖。我會調查。 – Pieter 2012-08-17 11:01:57

+0

讓我知道它是如何:) – FaddishWorm 2012-08-17 11:04:26

+0

我確保沒有兩個GET請求可以在同一時間啓動,但仍有可能提交一個新的GET請求,而第一個請求尚未收到響應。有沒有辦法取消舊的GET請求(從而關閉打開的TCP連接)以適應新的請求?我做了一些改動 - 其中一個就是像你所建議的那樣捕捉其他與QNetworkAccessManager相關的信號 - 但我仍然可以觸發這個錯誤。 – Pieter 2012-08-17 13:26:41