天天看點

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

接上面,進一步分析QNetworkAccessManager::createRequest()的實作。去除不重要的分支末節,看其調用的QNetworkReplyImplPrivate::setup()和QNetworkAccessManagerPrivate::findBackend()的代碼。

void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const QNetworkRequest &req,

                                     QIODevice *data)

{

    Q_Q(QNetworkReplyImpl);

    outgoingData = data;

    request = req;

    url = request.url();

    operation = op;

    if (outgoingData) {  // outgoingData實際就是QNetworkRequest對象

        q->connect(outgoingData, SIGNAL(readyRead()), SLOT(_q_sourceReadyRead()));

        q->connect(outgoingData, SIGNAL(readChannelFinished()), SLOT(_q_sourceReadChannelFinished()));

    }

    q->QIODevice::open(QIODevice::ReadOnly);  // ???

    QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);

}

連接配接兩個信号與槽之後,是打開QIODevice,暫未深入分析。然後是呼叫q->_q_startOperation(),實際就是調用QNetworkReplyImpl::_q_startOperation(),使用的是隊列等待方式(也就是發送一個消息進入系統消息隊列,這個setup函數以及全部後續執行完畢,主動權交回給Windows後,再根據進入隊列的消息來觸發)。

是以我們先看QNetworkAccessManagerPrivate::findBackend()的代碼實作:

QNetworkAccessBackend *QNetworkAccessManagerPrivate::findBackend(QNetworkAccessManager::Operation op,

                                                                 const QNetworkRequest &request)

    QNetworkRequest::CacheLoadControl mode =

        static_cast<QNetworkRequest::CacheLoadControl>(

            request.attribute(QNetworkRequest::CacheLoadControlAttribute,

                              QNetworkRequest::PreferNetwork).toInt());

    if (mode == QNetworkRequest::AlwaysCache

        && (op == QNetworkAccessManager::GetOperation

        || op == QNetworkAccessManager::HeadOperation)) {

        QNetworkAccessBackend *backend = new QNetworkAccessCacheBackend;

        backend->manager = this;

        return backend;

    if (!factoryDataShutdown) {

        QMutexLocker locker(&factoryData()->mutex);

        QNetworkAccessBackendFactoryData::ConstIterator it = factoryData()->constBegin(),

                                                           end = factoryData()->constEnd();

        while (it != end) {

            QNetworkAccessBackend *backend = (*it)->create(op, request);

            if (backend) {

                backend->manager = this;

                return backend; // found a factory that handled our request

            }

            ++it;

        }

    return 0;

這段代碼有一點複雜,先看紅色标記的第一句,factoryData()是用宏來定義的函數:

Q_GLOBAL_STATIC(QNetworkAccessBackendFactoryData, factoryData)

宏定義如下:

#define Q_GLOBAL_STATIC(TYPE, NAME)                              \

    static TYPE *NAME()                                          \

    {                                                            \

        static TYPE this_##NAME;                                 \

        static QGlobalStatic<TYPE > global_##NAME(&this_##NAME); \

        return global_##NAME.pointer;                            \

如果對STD比較熟悉,第一感覺這是一個模闆List操作。在這裡constBegin()和constEnd()組合起來是一個周遊,那麼在什麼地方設定值呢?良好代碼的命名是很規範的,我試了試全局查找factoryData(),找到了我所希望看到的東西:

QNetworkAccessBackendFactory::QNetworkAccessBackendFactory()

    QMutexLocker locker(&factoryData()->mutex);

    factoryData()->prepend(this);

QNetworkAccessBackendFactory::~QNetworkAccessBackendFactory()

        factoryData()->removeAll(this);

這裡prepend()應該是把對象添加到清單;而removeAll()就是清空全部資料了。

factoryData()裡面包含的對象序列,應該是從QNetworkAccessBackendFactory衍生出來的。

一共有哪些子類呢?繼續全局查找:

class QNetworkAccessDataBackendFactory: public QNetworkAccessBackendFactory

class QNetworkAccessDebugPipeBackendFactory: public QNetworkAccessBackendFactory

class QNetworkAccessFileBackendFactory: public QNetworkAccessBackendFactory

class QNetworkAccessFtpBackendFactory: public QNetworkAccessBackendFactory

class QNetworkAccessHttpBackendFactory : public QNetworkAccessBackendFactory

去除暫時不關心的DebugPipe,一共有四種:DataBackend、FileBackend、FtpBackend、HttpBackend。媒體的種類原來是在這裡實作的。看其中QNetworkAccessHttpBackendFactory::create()

QNetworkAccessBackend *

QNetworkAccessHttpBackendFactory::create(QNetworkAccessManager::Operation op,

                                         const QNetworkRequest &request) const

    // check the operation

    switch (op) {

    case QNetworkAccessManager::GetOperation:

    case QNetworkAccessManager::PostOperation:

    case QNetworkAccessManager::HeadOperation:

    case QNetworkAccessManager::PutOperation:

        break;

    default:

        // no, we can't handle this request

        return 0;

    QUrl url = request.url();

    QString scheme = url.scheme().toLower();

    if (scheme == QLatin1String("http") || scheme == QLatin1String("https"))

        return new QNetworkAccessHttpBackend;

如果是能夠處理的OP标記并且URL的字首是http或者是https,則建立一個QNetworkAccessHttpBackend對象。

前面QNetworkAccessManager::get()代碼中,調用的參數是QNetworkAccessManager::GetOperation,是以在我們分析的這個應用中,建立的是QNetworkAccessHttpBackend對象。

findBackend()到此分析完畢;由于factoryData()的具體實作跟我們分析網絡通信的目标沒有太大關系,未深入分析,有誰分析了的話請轉告一聲,值得一看。

回到前面暫停的QNetworkReplyImpl::_q_startOperation(),又實作了什麼動作呢?

void QNetworkReplyImplPrivate::_q_startOperation()

    // This function is called exactly once

    state = Working;

    if (!backend) {

        error(QNetworkReplyImpl::ProtocolUnknownError,

              QCoreApplication::translate("QNetworkReply", "Protocol \"%1\" is unknown").arg(url.scheme())); // not really true!;

        finished();

        return;

    backend->open();

    if (state != Finished) {

        if (operation == QNetworkAccessManager::GetOperation)

            pendingNotifications.append(NotifyDownstreamReadyWrite);

        if (outgoingData) {

            _q_sourceReadyRead();

        handleNotifications();

首先調用了剛剛建立的QNetworkAccessHttpBackend::open(),然後是添加通知消息、調用_q_sourceReadyRead()、最後處理通知消息。

繼續閱讀