QT分析之网络编程(八)

简介: 话说昨日走到QNetworkReplyImplPrivate::_q_startOperation(),勾引出QNetworkAccessHttpBackend::open(),今日接着欣赏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;
            break;
        }
    }

    // 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);

#ifndef QT_NO_NETWORKPROXY
        http->setTransparentProxy(transparentProxy);
        http->setCacheProxy(cacheProxy);
#endif

        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;
#endif
        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);
        validateCache(httpRequest, loadedFromCache);
        break;

    case QNetworkAccessManager::PostOperation:
        invalidateCache();
        httpRequest.setOperation(QHttpNetworkRequest::Post);
        uploadDevice = new QNetworkAccessHttpBackendIODevice(this);
        break;

    case QNetworkAccessManager::PutOperation:
        invalidateCache();
        httpRequest.setOperation(QHttpNetworkRequest::Put);
        uploadDevice = new QNetworkAccessHttpBackendIODevice(this);
        break;

    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);
#ifndef QT_NO_OPENSSL
    if (pendingSslConfiguration)
        httpReply->setSslConfiguration(*pendingSslConfiguration);
    if (pendingIgnoreSslErrors)
        httpReply->ignoreSslErrors();
#endif

    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)
{
    Q_D(QHttpNetworkConnection);
    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);
        break;
    case QHttpNetworkRequest::NormalPriority:
    case QHttpNetworkRequest::LowPriority:
        lowPriorityQueue.prepend(pair);
        break;
    }
    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);
        return;
    }
    // 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];
            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);
                lowPriorityQueue.removeAt(j);
                return;
            }
        }
    }
}
按优先级次序发送请求。prepareRequest()设定HTTP请求的Header信息;关键是sendRequest()
bool QHttpNetworkConnectionPrivate::sendRequest(QAbstractSocket *socket)
{
    Q_Q(QHttpNetworkConnection);

    int i = indexOf(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);
#ifndef QT_NO_NETWORKPROXY
        QByteArray header = QHttpNetworkRequestPrivate::header(channels[i].request,
            (networkProxy.type() != QNetworkProxy::NoProxy));
#else
        QByteArray header = QHttpNetworkRequestPrivate::header(channels[i].request,
            false);
#endif
        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;
            break;
        }
        // write the initial chunk together with the headers
        // fall through
    }
    case WritingState: { // write the data
        QIODevice *data = channels[i].request.d->data;
        if (channels[i].reply->d_func()->requestDataBuffer.size())
            data = &channels[i].reply->d_func()->requestDataBuffer;
        if (!data || channels[i].bytesTotal == channels[i].written) {
            channels[i].state = WaitingState; // now wait for response
            break;
        }

        QByteArray chunk;
        chunk.resize(ChunkSize);
        qint64 readSize = data->read(chunk.data(), ChunkSize);
        if (readSize == -1) {
            // source has reached EOF
            channels[i].state = WaitingState; // now wait for response
        } 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);
        }
        break;
    }
    case WaitingState:
    case ReadingState:
    case Wait4AuthState:
        // ignore _q_bytesWritten in these states
        // fall through
    default:
        break;
    }
    return true;
}
跟QTcpSocket有关的调用总算出现了。分析到此结束。
目录
相关文章
|
1月前
|
网络协议 C++
C++ Qt开发:QTcpSocket网络通信组件
`QTcpSocket`和`QTcpServer`是Qt中用于实现基于TCP(Transmission Control Protocol)通信的两个关键类。TCP是一种面向连接的协议,它提供可靠的、双向的、面向字节流的通信。这两个类允许Qt应用程序在网络上建立客户端和服务器之间的连接。Qt 是一个跨平台C++图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置,实现图形化开发极大的方便了开发效率,本章将重点介绍如何运用`QTcpSocket`组件实现基于TCP的网络通信功能。
38 8
C++ Qt开发:QTcpSocket网络通信组件
|
5月前
|
存储 Cloud Native Linux
C++ QT 实时进行网络监测
C++ QT 实时进行网络监测
|
6月前
|
数据采集 数据安全/隐私保护
Haskell网络编程:从数据采集到图片分析
爬虫技术在当今信息时代中发挥着关键作用,用于从互联网上获取数据并进行分析。本文将介绍如何使用Haskell进行网络编程,从数据采集到图片分析,为你提供一个清晰的指南。
Haskell网络编程:从数据采集到图片分析
|
1月前
|
网络协议 网络安全 API
Qt 网络编程之美:探索 URL、HTTP、服务发现与请求响应
Qt 网络编程之美:探索 URL、HTTP、服务发现与请求响应
48 1
|
1月前
|
域名解析 缓存 网络协议
探索Qt 网络编程:网络地址与服务类全解析
探索Qt 网络编程:网络地址与服务类全解析
55 0
|
1月前
|
网络协议 安全 网络安全
Qt 套接字类(QTcpSocket和QUdpSocket)解密:迈向 Qt 网络编程之巅
Qt 套接字类(QTcpSocket和QUdpSocket)解密:迈向 Qt 网络编程之巅
103 0
|
1月前
|
安全 Linux 网络安全
Qt SSL/TLS 安全通信类:构建安全网络应用的关键组件
Qt SSL/TLS 安全通信类:构建安全网络应用的关键组件
64 0
|
1月前
|
存储 网络安全 C++
C++ Qt开发:QUdpSocket网络通信组件
Qt 是一个跨平台C++图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置,实现图形化开发极大的方便了开发效率,本章将重点介绍如何运用`QUdpSocket`组件实现基于UDP的网络通信功能。与`QTcpSocket`组件功能类似,`QUdpSocket`组件是 Qt 中用于实现用户数据报协议(UDP,User Datagram Protocol)通信的类。UDP 是一种无连接的、不可靠的数据传输协议,它不保证数据包的顺序和可靠性,但具有低延迟和简单的特点。
20 0
C++ Qt开发:QUdpSocket网络通信组件
|
1月前
|
开发框架 安全 网络安全
Qt5.14.2揭秘Qt与SSL/TLS的完美邂逅:打造坚不可摧的网络安全防线
Qt5.14.2揭秘Qt与SSL/TLS的完美邂逅:打造坚不可摧的网络安全防线
|
1月前
|
监控 网络安全 C++
Qt 5.14.2 网络编程揭秘:构建高效HTTP客户端与文件下载器
Qt 5.14.2 网络编程揭秘:构建高效HTTP客户端与文件下载器

推荐镜像

更多