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(), 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有关的调用总算出现了。分析到此结束。
http://blog.163.com/net_worm/blog/static/12770241920101513548567/
相关文章推荐
- QT分析之网络编程(1--4)
- 【Qt】Windows7 和 Ubuntu11 下安装配置 Qt 5.2.0
- qt中qDebug()无法输出解决办法
- Qt4.8.3移植总结
- QT 4.8 静态库编译方法
- Qt 内存管理机制
- Qt核心剖析: moc
- Qt核心剖析:信息隐藏(3)
- Qt核心剖析:信息隐藏(2)
- Qt核心剖析:信息隐藏(1)
- QT控件之状态栏
- QTP录制不了脚本常见问题解决方法
- Qt中PRO文件写法的详细介绍,很有用,很重要!
- Qt创建和使用动态链接库
- QT工程pro设置实践(with QtCreator)----非弄的像VS一样才顺手?
- qte for arm的编译过程
- QT pro工程文件介绍
- Qt之打包发布(NSIS详解)
- Qt开发:设置QMAKESPEC,生成不同平台的makefile
- QT_opengl_gluPerspective没有定义的处理方法