您的位置:首页 > 编程语言 > Qt开发

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

2014-02-25 16:05 477 查看


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, 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(), 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 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有关的调用总算出现了。分析到此结束。
http://blog.163.com/net_worm/blog/static/12770241920101513548567/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: