天天看点

QT分析之网络编程(八)

话说昨日走到QNetworkReplyImplPrivate::_q_startOperation(),勾引出QNetworkAccessHttpBackend::open(),今日接着欣赏QT之美丽。

void QNetworkAccessHttpBackend::open()

{

    QUrl url = request().url();

    bool encrypt = url.scheme().toLower() == QLatin1String("https");

    setAttribute(QNetworkRequest::ConnectionEncryptedAttribute, encrypt);

    // set the port number in the reply if it wasn't set

    url.setPort(url.port(encrypt ? DefaultHttpsPort : DefaultHttpPort));

    QNetworkProxy *theProxy = 0;

#ifndef QT_NO_NETWORKPROXY

    QNetworkProxy transparentProxy, cacheProxy;

    foreach (const QNetworkProxy &p, proxyList()) {

        // use the first proxy that works

        // for non-encrypted connections, any transparent or HTTP proxy

        // for encrypted, only transparent proxies

        if (!encrypt

            && (p.capabilities() & QNetworkProxy::CachingCapability)

            && (p.type() == QNetworkProxy::HttpProxy ||

                p.type() == QNetworkProxy::HttpCachingProxy)) {

            cacheProxy = p;

            transparentProxy = QNetworkProxy::NoProxy;

            theProxy = &cacheProxy;

            break;

        }

        if (p.isTransparentProxy()) {

            transparentProxy = p;

            cacheProxy = QNetworkProxy::NoProxy;

            theProxy = &transparentProxy;

    }

    // check if at least one of the proxies

    if (transparentProxy.type() == QNetworkProxy::DefaultProxy &&

        cacheProxy.type() == QNetworkProxy::DefaultProxy) {

        // unsuitable proxies

        error(QNetworkReply::ProxyNotFoundError,

              tr("No suitable proxy found"));

        finished();

        return;

#endif

    // check if we have an open connection to this host

    cacheKey = makeCacheKey(this, theProxy);

    QNetworkAccessCache *cache = QNetworkAccessManagerPrivate::getCache(this);

    if ((http = static_cast<QNetworkAccessHttpBackendCache *>(cache->requestEntryNow(cacheKey))) == 0) {

        // no entry in cache; create an object

        http = new QNetworkAccessHttpBackendCache(url.host(), url.port(), encrypt);

        http->setTransparentProxy(transparentProxy);

        http->setCacheProxy(cacheProxy);

        cache->addEntry(cacheKey, http);

    setupConnection();

    postRequest();

}

在这里跟QNetworkAccessHttpBackendCache类关联起来。先在全局表中查找,如果没有找到则新创建一个QNetworkAccessHttpBackendCache对象。接着setupConnection()里面就是把QNetworkAccessHttpBackend的信号和QNetworkAccessHttpBackendCache的槽连接起来;postRequest()所先看是否能在Cache中找到,没找到需要的内容则发送请求。

QNetworkAccessHttpBackendCache类有两个基类。

class QNetworkAccessHttpBackendCache: public QHttpNetworkConnection,

                                      public QNetworkAccessCache::CacheableObject

在QHttpNetworkConnection的构造中,有些我们感兴趣的东西:

QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16 port, bool encrypt, QObject *parent)

    : QObject(*(new QHttpNetworkConnectionPrivate(hostName, port, encrypt)), parent)

    Q_D(QHttpNetworkConnection);

    d->init();

继续深入看QHttpNetorkConnectionPrivate::init()

void QHttpNetworkConnectionPrivate::init()

    for (int i = 0; i < channelCount; ++i) {

#ifndef QT_NO_OPENSSL

        channels[i].socket = new QSslSocket;

#else

        channels[i].socket = new QTcpSocket;

        connectSignals(channels[i].socket);

初始化的时候创建了QTcpSocket对象。

回到前面,继续看postRequst又做了哪些事情呢?

void QNetworkAccessHttpBackend::postRequest()

    bool loadedFromCache = false;

    QHttpNetworkRequest httpRequest;

    switch (operation()) {

    case QNetworkAccessManager::GetOperation:

        httpRequest.setOperation(QHttpNetworkRequest::Get);

        validateCache(httpRequest, loadedFromCache);

        break;

    case QNetworkAccessManager::HeadOperation:

        httpRequest.setOperation(QHttpNetworkRequest::Head);

    case QNetworkAccessManager::PostOperation:

        invalidateCache();

        httpRequest.setOperation(QHttpNetworkRequest::Post);

        uploadDevice = new QNetworkAccessHttpBackendIODevice(this);

    case QNetworkAccessManager::PutOperation:

        httpRequest.setOperation(QHttpNetworkRequest::Put);

    default:

        break;                  // can't happen

    httpRequest.setData(uploadDevice);

    httpRequest.setUrl(url());

    QList<QByteArray> headers = request().rawHeaderList();

    foreach (const QByteArray &header, headers)

        httpRequest.setHeaderField(header, request().rawHeader(header));

    if (loadedFromCache) {

        QNetworkAccessBackend::finished();

        return;    // no need to send the request! :)

    httpReply = http->sendRequest(httpRequest);

    httpReply->setParent(this);

    if (pendingSslConfiguration)

        httpReply->setSslConfiguration(*pendingSslConfiguration);

    if (pendingIgnoreSslErrors)

        httpReply->ignoreSslErrors();

    connect(httpReply, SIGNAL(readyRead()), SLOT(replyReadyRead()));

    connect(httpReply, SIGNAL(finished()), SLOT(replyFinished()));

    connect(httpReply, SIGNAL(finishedWithError(QNetworkReply::NetworkError,QString)),

            SLOT(httpError(QNetworkReply::NetworkError,QString)));

    connect(httpReply, SIGNAL(headerChanged()), SLOT(replyHeaderChanged()));

完了下面这些动作:

1、看Cache中是否保存有过去浏览的内容,如果有还要看是否超出生存时间(Expiration Time);

2、设定Url、Header和数据内容(需要提交的数据);

3、调用QNetworkAccessHttpBackendCache::sendRequest()发送请求内容;

4、把QHttpNetworkReply的信号与QNetworkAccessHttpBackend的槽连接起来,完成后续处理。

重点看QNetworkAccessHttpBackendCache::sendRequest()的实现,QNetworkAccessHttpBackendCache类本身没有sendRequest()成员函数,其定义在QHttpNetworkConnection::sendRequest()。

QHttpNetworkReply* QHttpNetworkConnection::sendRequest(const QHttpNetworkRequest &request)

    return d->queueRequest(request);

只是简单的调用QHttpNetworkConnectionPrivate::queueRequest()

QHttpNetworkReply* QHttpNetworkConnectionPrivate::queueRequest(const QHttpNetworkRequest &request)

    Q_Q(QHttpNetworkConnection);

    // The reply component of the pair is created initially.

    QHttpNetworkReply *reply = new QHttpNetworkReply(request.url());

    reply->setRequest(request);

    reply->d_func()->connection = q;

    HttpMessagePair pair = qMakePair(request, reply);

    switch (request.priority()) {

    case QHttpNetworkRequest::HighPriority:

        highPriorityQueue.prepend(pair);

    case QHttpNetworkRequest::NormalPriority:

    case QHttpNetworkRequest::LowPriority:

        lowPriorityQueue.prepend(pair);

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

    return reply;

发现QHttpNetworkConnection、QHttpNetworkRequest、QHttpNetworkReply、QHttpNetworkEngine跟之前的QNetworkConnection、QNetworkRequest、QNetworkReply很接近。

在这里整个消息处理(或者是初始化动作)完成之后,按消息序列调用QHttpNetworkConnectionPrivate::_q_startNextRequest()

其实现代码:

void QHttpNetworkConnectionPrivate::_q_startNextRequest()

    // send the current request again

    if (channels[0].resendCurrent || channels[1].resendCurrent) {

        int i = channels[0].resendCurrent ? 0:1;

        QAbstractSocket *socket = channels[i].socket;

        channels[i].resendCurrent = false;

        channels[i].state = IdleState;

        if (channels[i].reply)

            sendRequest(socket);

    // send the request using the idle socket

    QAbstractSocket *socket = channels[0].socket;

    if (isSocketBusy(socket)) {

        socket = (isSocketBusy(channels[1].socket) ? 0 :channels[1].socket);

    if (!socket) {

        return; // this will be called after finishing current request.

    unqueueRequest(socket);

void QHttpNetworkConnectionPrivate::unqueueRequest(QAbstractSocket *socket)

    Q_ASSERT(socket);

    int i = indexOf(socket);

    if (!highPriorityQueue.isEmpty()) {

        for (int j = highPriorityQueue.count() - 1; j >= 0; --j) {

            HttpMessagePair &messagePair = highPriorityQueue[j];

            if (!messagePair.second->d_func()->requestIsPrepared)

                prepareRequest(messagePair);

            if (!messagePair.second->d_func()->requestIsBuffering) {

                channels[i].request = messagePair.first;

                channels[i].reply = messagePair.second;

                sendRequest(socket);

                highPriorityQueue.removeAt(j);

                return;

            }

    if (!lowPriorityQueue.isEmpty()) {

        for (int j = lowPriorityQueue.count() - 1; j >= 0; --j) {

            HttpMessagePair &messagePair = lowPriorityQueue[j];

                lowPriorityQueue.removeAt(j);

按优先级次序发送请求。prepareRequest()设定HTTP请求的Header信息;关键是sendRequest()

bool QHttpNetworkConnectionPrivate::sendRequest(QAbstractSocket *socket)

    switch (channels[i].state) {

    case IdleState: { // write the header

        if (!ensureConnection(socket)) {

            // wait for the connection (and encryption) to be done

            // sendRequest will be called again from either

            // _q_connected or _q_encrypted

            return false;

        channels[i].written = 0; // excluding the header

        channels[i].bytesTotal = 0;

        if (channels[i].reply) {

            channels[i].reply->d_func()->clear();

            channels[i].reply->d_func()->connection = q;

            channels[i].reply->d_func()->autoDecompress = channels[i].request.d->autoDecompress;

        channels[i].state = WritingState;

        channels[i].pendingEncrypt = false;

        // if the url contains authentication parameters, use the new ones

        // both channels will use the new authentication parameters

        if (!channels[i].request.url().userInfo().isEmpty()) {

            QUrl url = channels[i].request.url();

            QAuthenticator &auth = channels[i].authenticator;

            if (url.userName() != auth.user()

                || (!url.password().isEmpty() && url.password() != auth.password())) {

                auth.setUser(url.userName());

                auth.setPassword(url.password());

                copyCredentials(i, &auth, false);

            // clear the userinfo,  since we use the same request for resending

            // userinfo in url can conflict with the one in the authenticator

            url.setUserInfo(QString());

            channels[i].request.setUrl(url);

        createAuthorization(socket, channels[i].request);

        QByteArray header = QHttpNetworkRequestPrivate::header(channels[i].request,

            (networkProxy.type() != QNetworkProxy::NoProxy));

            false);

        socket->write(header);

        QIODevice *data = channels[i].request.d->data;

        QHttpNetworkReply *reply = channels[i].reply;

        if (reply && reply->d_func()->requestDataBuffer.size())

            data = &channels[i].reply->d_func()->requestDataBuffer;

        if (data && (data->isOpen() || data->open(QIODevice::ReadOnly))) {

            if (data->isSequential()) {

                channels[i].bytesTotal = -1;

                QObject::connect(data, SIGNAL(readyRead()), q, SLOT(_q_dataReadyReadNoBuffer()));

                QObject::connect(data, SIGNAL(readChannelFinished()), q, SLOT(_q_dataReadyReadNoBuffer()));

            } else {

                channels[i].bytesTotal = data->size();

        } else {

            channels[i].state = WaitingState;

        // write the initial chunk together with the headers

        // fall through

    case WritingState: { // write the data

        if (channels[i].reply->d_func()->requestDataBuffer.size())

        if (!data || channels[i].bytesTotal == channels[i].written) {

            channels[i].state = WaitingState; // now wait for response

        QByteArray chunk;

        chunk.resize(ChunkSize);

        qint64 readSize = data->read(chunk.data(), ChunkSize);

        if (readSize == -1) {

            // source has reached EOF

        } else if (readSize > 0) {

            // source gave us something useful

            channels[i].written += socket->write(chunk.data(), readSize);

            if (channels[i].reply)

                emit channels[i].reply->dataSendProgress(channels[i].written, channels[i].bytesTotal);

    case WaitingState:

    case ReadingState:

    case Wait4AuthState:

        // ignore _q_bytesWritten in these states

    return true;

跟QTcpSocket有关的调用总算出现了。分析到此结束。