天天看點

QT分析之網絡程式設計(六)

在動手分析前,簡單介紹一下HTTP協定。HTTP協定是一種為分布式,合作式,超媒體資訊系統。它是一種通用的,無狀态(stateless)的協定,除了應用于超文本傳輸外,它也可以應用于諸如名稱伺服器和分布對象管理系統之類的系統,這可以通過擴充它的請求方法,錯誤代碼和報頭來實作。HTTP的一個特點是資料表現形式是可輸入的和可協商性的,這就允許系統能被建立而獨立于資料傳輸。HTTP在1990年WWW全球資訊剛剛起步的時候就得到了應用。該規範定義的協定用“HTTP/1.1”表示,是對RFC2608[33]的更新。

HTTP協定是通過定義一序列的動作(協定文本中稱為方法),來完成資料的傳輸通信。HTTP1.1版本中有這些方法:get、post、head、options、put、delete、trace、connect。

get方法用于擷取URI資源,是最為常用的一種方法。

post方法用于向指定URI送出内容,伺服器端響應其行為,該方法也極為常用。

head方法向URI發送請求,僅僅隻需要獲得響應的協定頭。

put方法用于向URI發送請求,若URI不存在,則要求伺服器端根據請求建立資源。當URI存在時,伺服器端必須接受請求内容,将其作為URI資源的修改後版本。

delete方法用于删除URI辨別的指定資源。

trace方法用于激活伺服器端對請求的循環回報,回報作為http響應的正文内容被傳輸回用戶端。

connect方法通常被用于使用代理連接配接。

更詳細的内容請檢視相關資料。

回到QT系統,manager->get()調用其實就是HTTP/1.1協定中get方法的實作。

QNetworkReply *QNetworkAccessManager::get(const QNetworkRequest &request)

{

    return d_func()->postProcess(createRequest(QNetworkAccessManager::GetOperation, request));

}

上面的一行程式中有兩個調用:

1、QNetworkAccessManager::createRequest()

2、QNetworkAccessManagerPrivate::postProcess()

先來看createRequest(),兩個參數:第一個參數表示使用Get方法;第二個參數是目标網址。

QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Operation op,

                                                    const QNetworkRequest &req,

                                                    QIODevice *outgoingData)

    Q_D(QNetworkAccessManager);

    QNetworkRequest request = req;

    if (!request.header(QNetworkRequest::ContentLengthHeader).isValid() &&

        outgoingData && !outgoingData->isSequential()) {

        // request has no Content-Length

        // but the data that is outgoing is random-access

        request.setHeader(QNetworkRequest::ContentLengthHeader, outgoingData->size());

    }

    if (d->cookieJar) {

        QList<QNetworkCookie> cookies = d->cookieJar->cookiesForUrl(request.url());

        if (!cookies.isEmpty())

            request.setHeader(QNetworkRequest::CookieHeader, qVariantFromValue(cookies));

    // first step: create the reply

    QUrl url = request.url();

    QNetworkReplyImpl *reply = new QNetworkReplyImpl(this);

    QNetworkReplyImplPrivate *priv = reply->d_func();

    priv->manager = this;

    // second step: fetch cached credentials

    QNetworkAuthenticationCredential *cred = d->fetchCachedCredentials(url);

    if (cred) {

        url.setUserName(cred->user);

        url.setPassword(cred->password);

        priv->urlForLastAuthentication = url;

    // third step: setup the reply

    priv->setup(op, request, outgoingData);

#ifndef QT_NO_NETWORKPROXY

    QList<QNetworkProxy> proxyList = d->queryProxy(QNetworkProxyQuery(request.url()));

    priv->proxyList = proxyList;

#endif

    // fourth step: find a backend

    priv->backend = d->findBackend(op, request);

    if (priv->backend) {

        priv->backend->setParent(reply);

        priv->backend->reply = priv;

#ifndef QT_NO_OPENSSL

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

    return reply;

代碼比較長,主要做了這些事情:

1、設定HTTP請求的頭資訊(例如用戶端請求内容的長度、Cookie等)

2、生成并初始化Reply對象(實際是QNetworkReplyImpl對象)

3、擷取本地緩存的認證資訊(如果有的話)

4、設定Reply

5、擷取一個backend實體

6、如果支援OPENSSL的話,設定SSL的配置

暫時先放一邊後面再對createRequest()做進一步的分析,再來看postProcess()

QNetworkReply *QNetworkAccessManagerPrivate::postProcess(QNetworkReply *reply)

    Q_Q(QNetworkAccessManager);

    QNetworkReplyPrivate::setManager(reply, q);

    q->connect(reply, SIGNAL(finished()), SLOT(_q_replyFinished()));

    /* In case we're compiled without SSL support, we don't have this signal and we need to

     * avoid getting a connection error. */

    q->connect(reply, SIGNAL(sslErrors(QList<QSslError>)), SLOT(_q_replySslErrors(QList<QSslError>)));

簡單來說就做了一件事情,把QNetworkReply的信号(finished、sslErrors)與QNetworkAccessManager的槽連接配接起來。

繼續閱讀