Qt HTTP源碼分析(一)

最近在做一個(gè)PC端的數(shù)據(jù)采集并帶有一定互動(dòng)功能的小程序唱凯,需要與后臺(tái)進(jìn)行通信,從后臺(tái)下載資源(視頻谎痢、圖片等)磕昼。在使用Qt的網(wǎng)絡(luò)庫(kù)實(shí)現(xiàn)http網(wǎng)絡(luò)通信的時(shí)候出現(xiàn)了一點(diǎn)問(wèn)題(問(wèn)題描述:使用QNetworkAccessManager的 finished(QNetworkReply *reply)信號(hào)來(lái)處理已經(jīng)發(fā)送的請(qǐng)求的結(jié)果時(shí),如果多個(gè)請(qǐng)求同時(shí)發(fā)送节猿,使用這種方式僅能收到其中一個(gè)請(qǐng)求的reply)票从,因此去查閱了Qt有關(guān)網(wǎng)絡(luò)部分的源碼,問(wèn)題的解決得益于分析源碼的過(guò)程滨嘱。
本次分析的是qtbase-opensource-src-5.8.0版本中網(wǎng)絡(luò)部分的源碼峰鄙,對(duì)比之前Qt4版本的源碼發(fā)生了比較大的改變,Qt5中http/https與其他的協(xié)議(ftp太雨、spdy等)的實(shí)現(xiàn)分離吟榴,http2.0也是單獨(dú)實(shí)現(xiàn),http完全從backend的抽象工廠模式中剝離出來(lái)囊扳,新的http采用外觀模式將http部分的實(shí)現(xiàn)隱藏吩翻,在源碼內(nèi)部?jī)H暴露出QHttpThreadDelegate,通過(guò)QHttpThreadDelegate與QNetworkReplyHttpImpl之間跨線程的信號(hào)槽來(lái)實(shí)現(xiàn)通信以及數(shù)據(jù)交互锥咸。

image.png

從Qt的幫助文檔中可以了解到http部分對(duì)用戶(hù)開(kāi)放的接口情況狭瞎,QNetworkAccessManager、QNetworkRequest搏予、QNetworkReply三個(gè)類(lèi)包裝在Qt上完成http通信所需要使用的接口熊锭。
QNetworkAccessManager -- 負(fù)責(zé)請(qǐng)求的創(chuàng)建以及狀態(tài)管理
QNetworkRequest -- 負(fù)責(zé)包裝http請(qǐng)求,包括設(shè)置協(xié)議頭雪侥、解析協(xié)議頭
QNetworkReply -- 負(fù)責(zé)接收對(duì)應(yīng)請(qǐng)求的返回信息
關(guān)于API的使用就不再贅述碗殷,Qt的文檔中有非常詳細(xì)的說(shuō)明與例子可以參考。下面對(duì)Qt內(nèi)部的實(shí)現(xiàn)流程進(jìn)行分析速缨。

1.QNetworkAccessManager --

createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest &req, QIODevice *outgoingData)

返回一個(gè)QNetworkReply 對(duì)象來(lái)處理網(wǎng)絡(luò)請(qǐng)求锌妻,參數(shù)outgoingData是put/post需要上傳的數(shù)據(jù),傳入QJson類(lèi)型需要轉(zhuǎn)為QByteArray鸟廓,在put/post內(nèi)部轉(zhuǎn)為QBuffer(輸出流對(duì)象)再傳給createRequest从祝,默認(rèn)情況下會(huì)調(diào)用QNetworkCookieJar::cookiesForUrl()來(lái)保存請(qǐng)求的信息。

1)http/https 的創(chuàng)建方式

    // Since Qt 5 we use the new QNetworkReplyHttpImpl
    if (scheme == QLatin1String("http") || scheme == QLatin1String("preconnect-http")
        || scheme == QLatin1String("https") || scheme == QLatin1String("preconnect-https")) {

        QNetworkReplyHttpImpl *reply = new QNetworkReplyHttpImpl(this, request, op, outgoingData);
        connect(this, SIGNAL(networkSessionConnected()),
                reply, SLOT(_q_networkSessionConnected()));
        return reply;
    }

2)其他協(xié)議的創(chuàng)建方式

    // first step: create the reply
    QNetworkReplyImpl *reply = new QNetworkReplyImpl(this);
    if (!isLocalFile) {
        connect(this, SIGNAL(networkSessionConnected()),
                reply, SLOT(_q_networkSessionConnected()));
    }
    QNetworkReplyImplPrivate *priv = reply->d_func();
    priv->manager = this;

    // second step: fetch cached credentials
    // This is not done for the time being, we should use signal emissions to request
    // the credentials from cache.
    // third step: find a backend
    priv->backend = d->findBackend(op, request);

    if (priv->backend) {
        priv->backend->setParent(reply);
        priv->backend->reply = priv;
    }

    reply->setSslConfiguration(request.sslConfiguration());

    // fourth step: setup the reply
    priv->setup(op, request, outgoingData);

i)在當(dāng)前版本的源碼中已經(jīng)找不到QNetworkAccessHttpBackend的工廠類(lèi)實(shí)現(xiàn)了引谜,在同步的http請(qǐng)求中依然能夠看到相關(guān)的代碼牍陌,關(guān)于Backend工廠的創(chuàng)建有一段比較有意思的包含了原子操作的代碼(個(gè)人猜測(cè)用于實(shí)現(xiàn)享元模式,還有待仔細(xì)分析)员咽。我們使用的http請(qǐng)求默認(rèn)為異步請(qǐng)求毒涧,而且Qt沒(méi)有開(kāi)放synchronous的設(shè)置,即我們提交的所有http/https請(qǐng)求都是異步處理的(源碼中關(guān)于同步請(qǐng)求部分的處理可以暫時(shí)忽略)贝室。
ii)可以看出QNetworkReplyHttpImpl 是作為通信中轉(zhuǎn)以及數(shù)據(jù)交互非常重要的類(lèi)契讲。

2.QNetworkReplyHttpImpl --

postRequest(const QNetworkRequest &newHttpRequest)
image.png
QHttpThreadDelegate *delegate = new QHttpThreadDelegate;
    // deleteLater 可以安全的刪除delegate
    QObject::connect(thread, SIGNAL(finished()), delegate, SLOT(deleteLater()));
        //通信狀態(tài)以及數(shù)據(jù)交互
        ...
        // 用于開(kāi)始請(qǐng)求.
        QObject::connect(q, SIGNAL(startHttpRequest()), delegate, SLOT(startRequest()));
        QObject::connect(q, SIGNAL(abortHttpRequest()), delegate, SLOT(abortRequest()));

        // 控制連接的緩沖區(qū)
        QObject::connect(q, SIGNAL(readBufferSizeChanged(qint64)), delegate, SLOT(readBufferSizeChanged(qint64)));
        QObject::connect(q, SIGNAL(readBufferFreed(qint64)), delegate, SLOT(readBufferFreed(qint64)));

    // 移動(dòng) delegate 到 http thread
    delegate->moveToThread(thread);
    //轉(zhuǎn)入子線程處理請(qǐng)求
    emit q->startHttpRequest();

3.QHttpThreadDelegate --

startRequest()
image.png
    //檢查是否有本地?cái)?shù)據(jù)
    if (!connections.hasLocalData()) {
        connections.setLocalData(new QNetworkAccessCache());
    }

    QUrl urlCopy = httpRequest.url();
    urlCopy.setPort(urlCopy.port(ssl ? 443 : 80));
    // 一個(gè) http 對(duì)象實(shí)質(zhì)上就是 QHttpNetworkConnection
    httpConnection = static_cast<QNetworkAccessCachedHttpConnection *>(connections.localData()->requestEntryNow(cacheKey));

        if (httpConnection == 0) {
        // no entry in cache; create an object
#ifdef QT_NO_BEARERMANAGEMENT
        httpConnection = new QNetworkAccessCachedHttpConnection(urlCopy.host(), urlCopy.port(), ssl,
                                                                connectionType);
#else
        httpConnection = new QNetworkAccessCachedHttpConnection(urlCopy.host(), urlCopy.port(), ssl,
                                                                connectionType,
                                                                networkSession);
    
    //向httpConnection 傳遞請(qǐng)求
    httpReply = httpConnection->sendRequest(httpRequest);
    httpReply->setParent(this);
      
     //關(guān)聯(lián)reply中我們需要處理的信號(hào)并跳轉(zhuǎn)
     ...

QHttpThreadDelegate 中聚合了 QHttpNetworkRequest仿吞、QHttpNetworkReply、QNetworkAccessCachedHttpConnection捡偏、QNetworkSession唤冈、QNetworkAccessAuthenticationManager等。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末银伟,一起剝皮案震驚了整個(gè)濱河市你虹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌彤避,老刑警劉巖傅物,帶你破解...
    沈念sama閱讀 222,464評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異琉预,居然都是意外死亡董饰,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,033評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)圆米,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)卒暂,“玉大人,你說(shuō)我怎么就攤上這事榨咐〗槿矗” “怎么了谴供?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,078評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵块茁,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我桂肌,道長(zhǎng)数焊,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,979評(píng)論 1 299
  • 正文 為了忘掉前任崎场,我火速辦了婚禮佩耳,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘谭跨。我一直安慰自己干厚,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,001評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布螃宙。 她就那樣靜靜地躺著蛮瞄,像睡著了一般。 火紅的嫁衣襯著肌膚如雪谆扎。 梳的紋絲不亂的頭發(fā)上挂捅,一...
    開(kāi)封第一講書(shū)人閱讀 52,584評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音堂湖,去河邊找鬼闲先。 笑死状土,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的伺糠。 我是一名探鬼主播蒙谓,決...
    沈念sama閱讀 41,085評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼训桶!你這毒婦竟也來(lái)了彼乌?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 40,023評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤渊迁,失蹤者是張志新(化名)和其女友劉穎慰照,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體琉朽,經(jīng)...
    沈念sama閱讀 46,555評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡毒租,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,626評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了箱叁。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片墅垮。...
    茶點(diǎn)故事閱讀 40,769評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖耕漱,靈堂內(nèi)的尸體忽然破棺而出算色,到底是詐尸還是另有隱情,我是刑警寧澤螟够,帶...
    沈念sama閱讀 36,439評(píng)論 5 351
  • 正文 年R本政府宣布灾梦,位于F島的核電站,受9級(jí)特大地震影響妓笙,放射性物質(zhì)發(fā)生泄漏若河。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,115評(píng)論 3 335
  • 文/蒙蒙 一寞宫、第九天 我趴在偏房一處隱蔽的房頂上張望萧福。 院中可真熱鬧,春花似錦辈赋、人聲如沸鲫忍。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,601評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)悟民。三九已至,卻和暖如春焕蹄,著一層夾襖步出監(jiān)牢的瞬間逾雄,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,702評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鸦泳,地道東北人银锻。 一個(gè)月前我還...
    沈念sama閱讀 49,191評(píng)論 3 378
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像做鹰,于是被迫代替她去往敵國(guó)和親击纬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,781評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,318評(píng)論 25 707
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理钾麸,服務(wù)發(fā)現(xiàn)更振,斷路器,智...
    卡卡羅2017閱讀 134,714評(píng)論 18 139
  • 1.OkHttp源碼解析(一):OKHttp初階2 OkHttp源碼解析(二):OkHttp連接的"前戲"——HT...
    隔壁老李頭閱讀 20,875評(píng)論 24 176
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)饭尝、插件肯腕、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,124評(píng)論 4 61
  • 我要聽(tīng)世界上最美妙的聲音, 我要看遠(yuǎn)方最美的極光钥平; 我要穿著長(zhǎng)裙实撒, 穿梭在陽(yáng)光淡淡的樹(shù)林間, 去親吻那顆嫩綠葉子上...
    崔大福閱讀 342評(píng)論 0 4