QT分析之网络编程(5--8)

QT分析之网络编程(五)  

今天分析QNetworkAccessManager、QNetworkRequest和QNetworkReply组成的高级抽象API序列。在动手之前,把doc中有关QNetworkAccessManager的介绍看了一遍。其使用方法大致是:
QNetworkAccessManager * manager = new QNetworkAccessManager(this);
QNetworkRequest request;
request.setUrl(QUrl("http://www.baidu.com"));
QNetworkReply * reply = manager->get(request);
connect(reply, SIGNAL(readyRead()), this, SLOT(slotReadyRead()));
关键是后面的三行:设定URL、发送并获取响应、读取数据。
在QT自带的例子中也有QNetworkAccessManager的应用:downloadmanager
单步跟踪就用downloadmanager这个例子。
在动手跟踪之前,总结了几个问题:
1、QNetworkAccessManager是更高级的抽象,那么怎么跟QTcpSocket/QUdpSocket联系起来的呢?
2、如果没有跟QTcpSocket联系起来,那么又是怎么跟WSA序列WinAPI联系起来的呢?
3、整个逻辑过程是怎么的呢?
4、获取的(图片或者网页)数据保存在什么地方?
5、跟HTTP或者FTP有关的Cookie、认证等怎么实现的?
6、HTTP的Session相关功能实现了吗?怎么实现的?

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());
#endif
    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()));
#ifndef QT_NO_OPENSSL
    /* 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>)));
#endif

    return reply;
}
简单来说就做了一件事情,把QNetworkReply的信号(finished、sslErrors)与QNetworkAccessManager的槽连接起来。
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()
{
    if (!factoryDataShutdown) {
        QMutexLocker locker(&factoryData()->mutex);
        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;

    return 0;
}
如果是能够处理的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()、最后处理通知消息。

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, on ly 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 on e 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(), encryp t);

#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 on es
        // 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 on e 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 *da ta = channels[i].request.d->da ta;
        QHttpNetworkReply *reply = channels[i].reply;
        if (reply && reply->d_func()->requestDataBuffer.size())
            da ta = &channels[i].reply->d_func()->requestDataBuffer;
        if (da ta && (da ta->isOpen() || da ta->open(QIODevice::ReadOnly))) {
            if (da ta->isSequential()) {
                channels[i].bytesTotal = -1;
                QObject::connect(da ta, SIGNAL(readyRead()), q, SLOT(_q_dataReadyReadNoBuffer()));
                QObject::connect(da ta, SIGNAL(readChannelFinished()), q, SLOT(_q_dataReadyReadNoBuffer()));
            } else {
                channels[i].bytesTotal = da ta->size();
            }
        } else {
            channels[i].state = WaitingState;
            break;
        }
        // write the initial chunk together with the headers
        // fall through
    }
    case WritingState: { // write the da ta
        QIODevice *da ta = channels[i].request.d->da ta;
        if (channels[i].reply->d_func()->requestDataBuffer.size())
            da ta = &channels[i].reply->d_func()->requestDataBuffer;
        if (!da ta || 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有关的调用总算出现了。分析到此结束。

http://blog.163.com/net_worm/blog/static/12770241920101513548567/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值