博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
QT分析之网络编程
阅读量:7097 次
发布时间:2019-06-28

本文共 101511 字,大约阅读时间需要 338 分钟。

原文地址:http://blog.163.com/net_worm/blog/static/127702419201002842553382/

 

首先对Windows下的网络编程总结一下:

如果是服务器,其WinSDK调用分别为:

1 WSAStartup() -> socket() -> htons() / htonl() -> bind() -> listen() -> accept() -> recv() / send() -> closesocket() -> WSACleanup()

如果是客户端程序,其调用序列为:

1 WSAStartup() -> socket() -> htons() / htonl() -> connect() -> recv() / send() -> closesocket() -> WSACleanup()

前面转贴的客户端(WinSocket温习)程序中,收到信息就在console打印出来然后退出了;在一般的应用中,通常是要一直等待收发消息的,直到程序确认退出才关闭socket。如果用一个轮询就会占用很多的CPU资源,所以很多嵌入式设计中会用一个WaitForMultiObject调用,等待退出命令或者超时,然后退出或进行下一轮信息接受。在Windows平台下也有一些比较高明的设计,使用异步socket,然后用异步选择的办法,实现多线程和事件的并发。在WinSocket中,同样也有一套异步Socket函数,那就是使用WSAAsyncSelect()及其配合函数。具体可以参考MSDN。QT在Windows平台上的实现,肯定也跟这些SDK调用有关。

按照这个思路,果然在QT代码里面找到了Qnativesocketengine_win.cpp,WSAStartup(),WSASocket()等序列WSA函数都有。QNativeSocketEnginePrivate类把这些SDK封装成:createNewSocket()、option()、setOption()、nativeConnect()、nativeBind()、nativeListen()、nativeAccept()、nativeWrite()、nativeRead()、nativeSelect()、nativeClose()等。按照QT的设计,QPrivate类是数据类;Q类应该是主类。接着看QNativeSocket类的继承:

1 QNativeSocketEngine : public QAbstractSocketEngine : public QObject

QAbstractSocketEngine类是使用了大量纯虚函数的定义。继续深入查看,发现大量有关的类:QAbstractSocket,SocketAsyncHandler,QTcpSocket,QUdpSocket等,看来我要先了解下QT网络编程体系再进一步分析之

 

之前没有看QT自带的文档,看了doc之后对QT的网络体系有一个大致的了解:

QNatvieSocketEnginePrivate是OS相关的API封装,和QNativeSocketEngine一起构成具体平台SOCKET实现;
QTcpSocket、QUdpSocket、QTcpServer构成底层的应用API;QSslSocket是SSL加密相关API;
QHttp、QFtp构成高层次应该API;
QNetworkAccessManager、QNetworkRequest、QNetworkReply是高度抽象的网络层。
分析TCP的例子fortuneclient,运行起来按了[Get
Fortune]按钮之后,调用的是Client::requestNewFortune()。

 

1 void Client::requestNewFortune()2 {3     getFortuneButton->setEnabled(false);4     blockSize = 0;5     tcpSocket->abort();6     tcpSocket->connectToHost(hostLineEdit->text(), portLineEdit->text().toInt());7 }

具体看QTcpSocket::connectToHost()的代码

 

1 void QAbstractSocket::connectToHost(const QString &hostName, quint16 port,2                                     OpenMode openMode)3 {4     QMetaObject::invokeMethod(this, "connectToHostImplementation",5                               Qt::DirectConnection,6                               Q_ARG(QString, hostName),7                               Q_ARG(quint16, port),8                               Q_ARG(OpenMode, openMode));9 }

 

调用的是QAbstractSocket::connectToHostImplementation()。

 

1 void QAbstractSocket::connectToHostImplementation(const QString &hostName, quint16 port, 2                                                   OpenMode openMode) 3 { 4     Q_D(QAbstractSocket); 5     if (d->state == ConnectedState || d->state == ConnectingState || d->state == ClosingState) { 6         qWarning("QAbstractSocket::connectToHost() called when already connecting/connected to \"%s\"", qPrintable(hostName)); 7         return; 8     } 9 10     d->hostName = hostName;11     d->port = port;12     d->state = UnconnectedState;13     d->readBuffer.clear();14     d->writeBuffer.clear();15     d->abortCalled = false;16     d->closeCalled = false;17     d->pendingClose = false;18     d->localPort = 0;19     d->peerPort = 0;20     d->localAddress.clear();21     d->peerAddress.clear();22     d->peerName = hostName;23     if (d->hostLookupId != -1) {24         QHostInfo::abortHostLookup(d->hostLookupId);25         d->hostLookupId = -1;26     }27 28 #ifndef QT_NO_NETWORKPROXY29     // Get the proxy information30     d->resolveProxy(hostName, port);31     if (d->proxyInUse.type() == QNetworkProxy::DefaultProxy) {32         // failed to setup the proxy33         d->socketError = QAbstractSocket::UnsupportedSocketOperationError;34         setErrorString(QAbstractSocket::tr("Operation on socket is not supported"));35         emit error(d->socketError);36         return;37     }38 #endif39 40     if (!d_func()->isBuffered)41         openMode |= QAbstractSocket::Unbuffered;42     QIODevice::open(openMode);  // ??43     d->state = HostLookupState;44     emit stateChanged(d->state);45 46     QHostAddress temp;47     if (temp.setAddress(hostName)) {48         QHostInfo info;49         info.setAddresses(QList
() << temp);50 d->_q_startConnecting(info);51 #ifndef QT_NO_NETWORKPROXY52 } else if (d->proxyInUse.capabilities() & QNetworkProxy::HostNameLookupCapability) {53 // the proxy supports connection by name, so use it54 d->startConnectingByName(hostName);55 return;56 #endif57 } else {58 if (d->threadData->eventDispatcher)59 d->hostLookupId = QHostInfo::lookupHost(hostName, this, SLOT(_q_startConnecting(QHostInfo)));60 }61 }

 

继续调用QAbstractSocket::_q_startConnecting(),是QAbstractSocket的私有信号。简单来说,_q_startConnecting()就是调用了_q_connectToNextAddress()而已。

1 void QAbstractSocketPrivate::_q_connectToNextAddress() 2 { 3     Q_Q(QAbstractSocket); 4     do { 5         // Check for more pending addresses 6         if (addresses.isEmpty()) { 7             state = QAbstractSocket::UnconnectedState; 8             if (socketEngine) { 9                 if ((socketEngine->error() == QAbstractSocket::UnknownSocketError10                     ) && socketEngine->state() == QAbstractSocket::ConnectingState) {11                     socketError = QAbstractSocket::ConnectionRefusedError;12                     q->setErrorString(QAbstractSocket::tr("Connection refused"));13                 } else {14                     socketError = socketEngine->error();15                     q->setErrorString(socketEngine->errorString());16                 }17             } else {18 //                socketError = QAbstractSocket::ConnectionRefusedError;19 //                q->setErrorString(QAbstractSocket::tr("Connection refused"));20             }21             emit q->stateChanged(state);22             emit q->error(socketError);23             return;24         }25 26         // Pick the first host address candidate27         host = addresses.takeFirst();28 29 #if defined(QT_NO_IPV6)30         if (host.protocol() == QAbstractSocket::IPv6Protocol) {31             // If we have no IPv6 support, then we will not be able to32             // connect. So we just pretend we didn't see this address.33             continue;34         }35 #endif36 37         if (!initSocketLayer(host.protocol())) {38             // hope that the next address is better39             continue;40         }41 42         // Tries to connect to the address. If it succeeds immediately43         // (localhost address on BSD or any UDP connect), emit44         // connected() and return.45         if (socketEngine->connectToHost(host, port)) {46             //_q_testConnection();47             fetchConnectionParameters();48             return;49         }50 51         // cache the socket descriptor even if we're not fully connected yet52         cachedSocketDescriptor = socketEngine->socketDescriptor();53 54         // Check that we're in delayed connection state. If not, try55         // the next address56         if (socketEngine->state() != QAbstractSocket::ConnectingState) {57             continue;58         }59 60         // Start the connect timer.61         if (threadData->eventDispatcher) {62             if (!connectTimer) {63                 connectTimer = new QTimer(q);64                 QObject::connect(connectTimer, SIGNAL(timeout()),65                                  q, SLOT(_q_abortConnectionAttempt()),66                                  Qt::DirectConnection);67             }68             connectTimer->start(QT_CONNECT_TIMEOUT);69         }70 71         // Wait for a write notification that will eventually call72         // _q_testConnection().73         socketEngine->setWriteNotificationEnabled(true);74         break;75     } while (state != QAbstractSocket::ConnectedState);76 }

上面关键的三句,实际是把WinSocket编程中的简单过程分成三个阶段:socket初始化;connect到远程目标;设定Timer定时查看并处理Select的情况(收发数据或者关闭socket)。这里主要看前面两个:初始化和连接,select的处理放到明天分析。

1、初始化

1 bool QAbstractSocketPrivate::initSocketLayer(QAbstractSocket::NetworkLayerProtocol protocol) 2 { 3 #ifdef QT_NO_NETWORKPROXY 4     // this is here to avoid a duplication of the call to createSocketEngine below 5     static const QNetworkProxy &proxyInUse = *(QNetworkProxy *)0; 6 #endif 7  8     Q_Q(QAbstractSocket); 9 10     resetSocketLayer();11     socketEngine = QAbstractSocketEngine::createSocketEngine(q->socketType(), proxyInUse, q);12     if (!socketEngine) {13         socketError = QAbstractSocket::UnsupportedSocketOperationError;14         q->setErrorString(QAbstractSocket::tr("Operation on socket is not supported"));15         return false;16     }17     if (!socketEngine->initialize(q->socketType(), protocol)) {18         socketError = socketEngine->error();19         q->setErrorString(socketEngine->errorString());20         return false;21     }22 23     if (threadData->eventDispatcher)24         socketEngine->setReceiver(this);25 26     return true;27 }28 29 QAbstractSocketEngine *QAbstractSocketEngine::createSocketEngine(QAbstractSocket::SocketType socketType, const QNetworkProxy &proxy, QObject *parent)30 {31 #ifndef QT_NO_NETWORKPROXY32     // proxy type must have been resolved by now33     if (proxy.type() == QNetworkProxy::DefaultProxy)34         return 0;35 #endif36 37     QMutexLocker locker(&socketHandlers()->mutex);38     for (int i = 0; i < socketHandlers()->size(); i++) {39         if (QAbstractSocketEngine *ret = socketHandlers()->at(i)->createSocketEngine(socketType, proxy, parent))40             return ret;41     }42 43     return new QNativeSocketEngine(parent);44 }

 

上面可以知道socketEngine->initialize()实际调用的是QNativeSocketEngine::initialize()

 

1 bool QNativeSocketEngine::initialize(QAbstractSocket::SocketType socketType, QAbstractSocket::NetworkLayerProtocol protocol) 2 { 3     Q_D(QNativeSocketEngine); 4     if (isValid()) 5         close(); 6  7 #if defined(QT_NO_IPV6) 8     if (protocol == QAbstractSocket::IPv6Protocol) { 9         d->setError(QAbstractSocket::UnsupportedSocketOperationError,10                     QNativeSocketEnginePrivate::NoIpV6ErrorString);11         return false;12     }13 #endif14 15     // Create the socket16     if (!d->createNewSocket(socketType, protocol)) {17         return false;18     }19 20     // Make the socket nonblocking.21     if (!setOption(NonBlockingSocketOption, 1)) {22         d->setError(QAbstractSocket::UnsupportedSocketOperationError,23                     QNativeSocketEnginePrivate::NonBlockingInitFailedErrorString);24         close();25         return false;26     }27 28     // Set the broadcasting flag if it's a UDP socket.29     if (socketType == QAbstractSocket::UdpSocket30         && !setOption(BroadcastSocketOption, 1)) {31         d->setError(QAbstractSocket::UnsupportedSocketOperationError,32                     QNativeSocketEnginePrivate::BroadcastingInitFailedErrorString);33         close();34         return false;35     }36 37     // Make sure we receive out-of-band data38     if (socketType == QAbstractSocket::TcpSocket39         && !setOption(ReceiveOutOfBandData, 1)) {40         qWarning("QNativeSocketEngine::initialize unable to inline out-of-band data");41     }42 43     // Set the send and receive buffer sizes to a magic size, found44     // most optimal for our platforms.45     setReceiveBufferSize(49152);46     setSendBufferSize(49152);47 48     d->socketType = socketType;49     d->socketProtocol = protocol;50     return true;51 }

 

至此,初始化过程完成,socket被设定为非阻塞模式(也就是Select会超时方式)。

2、connect到远程目标

1 bool QNativeSocketEngine::connectToHost(const QHostAddress &address, quint16 port) 2 { 3     Q_D(QNativeSocketEngine); 4     Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::connectToHost(), false); 5  6 #if defined (QT_NO_IPV6) 7     if (address.protocol() == QAbstractSocket::IPv6Protocol) { 8         d->setError(QAbstractSocket::UnsupportedSocketOperationError, 9                     QNativeSocketEnginePrivate::NoIpV6ErrorString);10         return false;11     }12 #endif13     if (!d->checkProxy(address))14         return false;15 16     Q_CHECK_STATES(QNativeSocketEngine::connectToHost(),17                    QAbstractSocket::UnconnectedState, QAbstractSocket::ConnectingState, false);18 19     d->peerAddress = address;20     d->peerPort = port;21     bool connected = d->nativeConnect(address, port);22     if (connected)23         d->fetchConnectionParameters();24 25     return connected;26 }

 

连接相对简单。

 

3、读取信息

在QAbstractSocket中,有两个成员是收发数据用的:readData()、writeData()

readData()有两种读取方式:有缓冲和无缓冲方式。基本原理是一致的,简单其见只分析无缓冲直接读取方式。

1 qint64 QAbstractSocket::readData(char *data, qint64 maxSize) 2 { 3     Q_D(QAbstractSocket); 4     if (d->socketEngine && !d->socketEngine->isReadNotificationEnabled() && d->socketEngine->isValid()) 5         d->socketEngine->setReadNotificationEnabled(true); 6  7     if (!d->isBuffered) { 8         if (!d->socketEngine) 9             return -1;          // no socket engine is probably EOF10         qint64 readBytes = d->socketEngine->read(data, maxSize);11         if (readBytes < 0) {12             d->socketError = d->socketEngine->error();13             setErrorString(d->socketEngine->errorString());14         }15         if (!d->socketEngine->isReadNotificationEnabled())16             d->socketEngine->setReadNotificationEnabled(true);17         return readBytes;18     }19 20     if (d->readBuffer.isEmpty())21         // if we're still connected, return 0 indicating there may be more data in the future22         // if we're not connected, return -1 indicating EOF23         return d->state == QAbstractSocket::ConnectedState ? qint64(0) : qint64(-1);24 25     // If readFromSocket() read data, copy it to its destination.26     if (maxSize == 1) {27         *data = d->readBuffer.getChar();28         return 1;29     }30 31     qint64 bytesToRead = qMin(qint64(d->readBuffer.size()), maxSize);32     qint64 readSoFar = 0;33     while (readSoFar < bytesToRead) {34         const char *ptr = d->readBuffer.readPointer();35         int bytesToReadFromThisBlock = qMin(int(bytesToRead - readSoFar),36                                             d->readBuffer.nextDataBlockSize());37         memcpy(data + readSoFar, ptr, bytesToReadFromThisBlock);38         readSoFar += bytesToReadFromThisBlock;39         d->readBuffer.free(bytesToReadFromThisBlock);40     }41 42     return readSoFar;43 }

 

从前面(二)可以知道,socketEngine->read()实际调用的是QNativeSocketEngine::read()

1 qint64 QNativeSocketEngine::read(char *data, qint64 maxSize) 2 { 3     Q_D(QNativeSocketEngine); 4     Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::read(), -1); 5     Q_CHECK_STATES(QNativeSocketEngine::read(), QAbstractSocket::ConnectedState, QAbstractSocket::BoundState, -1); 6  7     qint64 readBytes = d->nativeRead(data, maxSize); 8  9     // Handle remote close10     if (readBytes == 0 && d->socketType == QAbstractSocket::TcpSocket) {11         d->setError(QAbstractSocket::RemoteHostClosedError,12                     QNativeSocketEnginePrivate::RemoteHostClosedErrorString);13         close();14         return -1;15     }16     return readBytes;17 }

 

除了一些相关的检查,就是调用QNativeSocketPrivate::nativeRead()

1 qint64 QNativeSocketEnginePrivate::nativeRead(char *data, qint64 maxLength) 2 { 3     qint64 ret = -1; 4     WSABUF buf; 5     buf.buf = data; 6     buf.len = maxLength; 7     DWORD flags = 0; 8     DWORD bytesRead = 0; 9 #if defined(Q_OS_WINCE)10     WSASetLastError(0);11 #endif12     if (::WSARecv(socketDescriptor, &buf, 1, &bytesRead, &flags, 0,0) ==  SOCKET_ERROR) {13         int err = WSAGetLastError();14         WS_ERROR_DEBUG(err);15         switch (err) {16         case WSAEWOULDBLOCK:17             ret = -2;18             break;19         case WSAEBADF:20         case WSAEINVAL:21             setError(QAbstractSocket::NetworkError, ReadErrorString);22             break;23         case WSAECONNRESET:24         case WSAECONNABORTED:25             // for tcp sockets this will be handled in QNativeSocketEngine::read26             ret = 0;27             break;28         default:29             break;30         }31     } else {32         if (WSAGetLastError() == WSAEWOULDBLOCK)33             ret = -2;34         else35             ret = qint64(bytesRead);36     }37 38     return ret;39 }

 

至此,调用Windows API读取数据。

 

4、发送数据

同样分有缓存与无缓存方式,对无缓存方式:

1 qint64 QAbstractSocket::writeData(const char *data, qint64 size) 2 { 3     Q_D(QAbstractSocket); 4     if (d->state == QAbstractSocket::UnconnectedState) { 5         d->socketError = QAbstractSocket::UnknownSocketError; 6         setErrorString(tr("Socket is not connected")); 7         return -1; 8     } 9 10     if (!d->isBuffered) {11         qint64 written = d->socketEngine->write(data, size);12         if (written < 0) {13             d->socketError = d->socketEngine->error();14             setErrorString(d->socketEngine->errorString());15         } else if (!d->writeBuffer.isEmpty()) {16             d->socketEngine->setWriteNotificationEnabled(true);17         }18         if (written >= 0)19             emit bytesWritten(written);20         return written;21     }22 23     char *ptr = d->writeBuffer.reserve(size);24     if (size == 1)25         *ptr = *data;26     else27         memcpy(ptr, data, size);28 29     qint64 written = size;30 31     if (d->socketEngine && !d->writeBuffer.isEmpty())32         d->socketEngine->setWriteNotificationEnabled(true);33     return written;34 }

查看QNativeSocketEngine::write():

1 qint64 QNativeSocketEngine::write(const char *data, qint64 size) 2 { 3     Q_D(QNativeSocketEngine); 4     Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::write(), -1); 5     Q_CHECK_STATE(QNativeSocketEngine::write(), QAbstractSocket::ConnectedState, -1); 6     return d->nativeWrite(data, size); 7 } 8  9 qint64 QNativeSocketEnginePrivate::nativeWrite(const char *data, qint64 len)10 {11     Q_Q(QNativeSocketEngine);12     qint64 ret = 0;13     // don't send more than 49152 per call to WSASendTo to avoid getting a WSAENOBUFS14     for (;;) {15         qint64 bytesToSend = qMin
(49152, len - ret);16 WSABUF buf;17 buf.buf = (char*)data + ret;18 buf.len = bytesToSend;19 DWORD flags = 0;20 DWORD bytesWritten = 0;21 22 int socketRet = ::WSASend(socketDescriptor, &buf, 1, &bytesWritten, flags, 0,0);23 24 ret += qint64(bytesWritten);25 26 if (socketRet != SOCKET_ERROR) {27 if (ret == len)28 break;29 else30 continue;31 } else if (WSAGetLastError() == WSAEWOULDBLOCK) {32 break;33 } else {34 int err = WSAGetLastError();35 WS_ERROR_DEBUG(err);36 switch (err) {37 case WSAECONNRESET:38 case WSAECONNABORTED:39 ret = -1;40 setError(QAbstractSocket::NetworkError, WriteErrorString);41 q->close();42 break;43 default:44 break;45 }46 break;47 }48 }49 return ret;50 }

 

至此分析完毕。

 

前面分析中,一个问题一直没有解决:新生成的SOCKET是什么时候加入WSASelect()的?另外还有一个不是很大的问题,close流程。

1 QEventDispatcherWin32Private::doWsaAsyncSelect()

中WSAAsyncSelect()设置一个断点,观察call stack:

1 QtCored4.dll!QEventDispatcherWin32Private::doWsaAsyncSelect(int socket=0x00001628)  行633    C++ 2      QtCored4.dll!QEventDispatcherWin32::registerSocketNotifier(QSocketNotifier * notifier=0x00c6f248)  行829    C++ 3      QtCored4.dll!QSocketNotifier::QSocketNotifier(int socket=0x00001628, QSocketNotifier::Type type=Write, QObject * parent=0x00c66228)  行185    C++ 4      QtNetworkd4.dll!QWriteNotifier::QWriteNotifier(int fd=0x00001628, QNativeSocketEngine * parent=0x00c66228)  行1053 + 0x1a 字节    C++ 5      QtNetworkd4.dll!QNativeSocketEngine::setWriteNotificationEnabled(bool enable=true)  行1118 + 0x2d 字节    C++ 6      QtNetworkd4.dll!QAbstractSocketPrivate::_q_connectToNextAddress()  行996    C++ 7      QtNetworkd4.dll!QAbstractSocketPrivate::_q_startConnecting(const QHostInfo & hostInfo={...})  行890    C++ 8      QtNetworkd4.dll!QAbstractSocket::qt_metacall(QMetaObject::Call _c=InvokeMetaMethod, int _id=0x0000000a, void * * _a=0x00c6e510)  行104 + 0x16 字节    C++ 9      QtNetworkd4.dll!QTcpSocket::qt_metacall(QMetaObject::Call _c=InvokeMetaMethod, int _id=0x00000012, void * * _a=0x00c6e510)  行58 + 0x14 字节    C++10      QtCored4.dll!QMetaCallEvent::placeMetaCall(QObject * object=0x00c4f790)  行478    C++11      QtCored4.dll!QObject::event(QEvent * e=0x00c4d8a0)  行1102 + 0x14 字节    C++12      QtGuid4.dll!QApplicationPrivate::notify_helper(QObject * receiver=0x00c4f790, QEvent * e=0x00c4d8a0)  行4065 + 0x11 字节    C++13      QtGuid4.dll!QApplication::notify(QObject * receiver=0x00c4f790, QEvent * e=0x00c4d8a0)  行3605 + 0x10 字节    C++14      QtCored4.dll!QCoreApplication::notifyInternal(QObject * receiver=0x00c4f790, QEvent * event=0x00c4d8a0)  行610 + 0x15 字节    C++15      QtCored4.dll!QCoreApplication::sendEvent(QObject * receiver=0x00c4f790, QEvent * event=0x00c4d8a0)  行213 + 0x39 字节    C++16      QtCored4.dll!QCoreApplicationPrivate::sendPostedEvents(QObject * receiver=0x00000000, int event_type=0x00000000, QThreadData * data=0x00bc8890)  行1247 + 0xd 字节    C++17      QtCored4.dll!QEventDispatcherWin32::processEvents(QFlags
flags={...}) 行679 + 0x10 字节 C++18 QtGuid4.dll!QGuiEventDispatcherWin32::processEvents(QFlags
flags={...}) 行1182 + 0x15 字节 C++19 QtCored4.dll!QEventLoop::processEvents(QFlags
flags={...}) 行150 C++20 QtCored4.dll!QEventLoop::exec(QFlags
flags={...}) 行201 + 0x2d 字节 C++21 QtGuid4.dll!QDialog::exec() 行499 C++22 fortuneclient.exe!main(int argc=0x00000001, char * * argv=0x00bc8750) 行51 + 0x9 字节 C++23 fortuneclient.exe!WinMain(HINSTANCE__ * instance=0x00400000, HINSTANCE__ * prevInstance=0x00000000, char * __formal=0x001520e2, int cmdShow=0x00000001) 行137 + 0x12 字节 C++24 fortuneclient.exe!__tmainCRTStartup() 行574 + 0x35 字节 C25 fortuneclient.exe!WinMainCRTStartup() 行399 C26 kernel32.dll!7c82f23b()

[下面的框架可能不正确和/或缺失,没有为 kernel32.dll 加载符号]   

 

看QNativeSocketEngine::setWriteNotificationEnabled()的代码实现:

1 void QNativeSocketEngine::setWriteNotificationEnabled(bool enable) 2 { 3     Q_D(QNativeSocketEngine); 4     if (d->writeNotifier) { 5         d->writeNotifier->setEnabled(enable); 6     } else if (enable && d->threadData->eventDispatcher) { 7         d->writeNotifier = new QWriteNotifier(d->socketDescriptor, this); 8         d->writeNotifier->setEnabled(true); 9     }10 }

 

在QWriteNotifier对象新建的时候,引起其父类的构建:QSocketNotifier

1 QSocketNotifier::QSocketNotifier(int socket, Type type, QObject *parent) 2     : QObject(parent) 3 { 4     if (socket < 0) 5         qWarning("QSocketNotifier: Invalid socket specified"); 6     sockfd = socket; 7     sntype = type; 8     snenabled = true; 9 10     Q_D(QObject);11     if (!d->threadData->eventDispatcher) {12         qWarning("QSocketNotifier: Can only be used with threads started with QThread");13     } else {14         d->threadData->eventDispatcher->registerSocketNotifier(this);15     }16 }

 

原来是通过获取当前线程数据得到Dispatcher的指针(QEventDispatcherWin32),通过其注册QNativeSocketEngine对象自己本身。

1 void QEventDispatcherWin32::registerSocketNotifier(QSocketNotifier *notifier) 2 { 3     Q_ASSERT(notifier); 4     int sockfd = notifier->socket(); 5     int type = notifier->type(); 6  7     Q_D(QEventDispatcherWin32); 8     QSNDict *sn_vec[3] = { &d->sn_read, &d->sn_write, &d->sn_except }; 9     QSNDict *dict = sn_vec[type];10 11     if (QCoreApplication::closingDown()) // ### d->exitloop?12         return; // after sn_cleanup, don't reinitialize.13 14     if (dict->contains(sockfd)) {15         const char *t[] = { "Read", "Write", "Exception" };16     /* Variable "socket" below is a function pointer. */17         qWarning("QSocketNotifier: Multiple socket notifiers for "18                  "same socket %d and type %s", sockfd, t[type]);19     }20 21     QSockNot *sn = new QSockNot;22     sn->obj = notifier;23     sn->fd  = sockfd;24     dict->insert(sn->fd, sn);25 26     if (d->internalHwnd)27         d->doWsaAsyncSelect(sockfd);28 }

 

在这里跟前面分析的QEventDispatcherWin32消息处理搭上关系了,把QWriteNotifier对象加入到系统的列表中;在QApplication::exec()的消息循环中,就能够获取目标对象了。

 

今天分析QNetworkAccessManager、QNetworkRequest和QNetworkReply组成的高级抽象API序列。在动手之前,把doc中有关QNetworkAccessManager的介绍看了一遍。其使用方法大致是:

1 QNetworkAccessManager * manager = new QNetworkAccessManager(this);2 QNetworkRequest request;3 request.setUrl(QUrl("http://www.baidu.com"));4 QNetworkReply * reply = manager->get(request);5 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相关功能实现了吗?怎么实现的?

 

在动手分析前,简单介绍一下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方法的实现。

1 QNetworkReply *QNetworkAccessManager::get(const QNetworkRequest &request)2 {3     return d_func()->postProcess(createRequest(QNetworkAccessManager::GetOperation, request));4 }

上面的一行程序中有两个调用

1 QNetworkAccessManager::createRequest()2 QNetworkAccessManagerPrivate::postProcess()

先来看createRequest(),两个参数:第一个参数表示使用Get方法;第二个参数是目标网址。

1 QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Operation op,  2                                                     const QNetworkRequest &req,  3                                                     QIODevice *outgoingData)  4 {  5     Q_D(QNetworkAccessManager);  6   7     bool isLocalFile = req.url().isLocalFile();  8     QString scheme = req.url().scheme().toLower();  9  10     // fast path for GET on file:// URLs 11     // The QNetworkAccessFileBackend will right now only be used for PUT 12     if ((op == QNetworkAccessManager::GetOperation || op == QNetworkAccessManager::HeadOperation) 13         && (isLocalFile || scheme == QLatin1String("qrc"))) { 14         return new QNetworkReplyFileImpl(this, req, op); 15     } 16  17     if ((op == QNetworkAccessManager::GetOperation || op == QNetworkAccessManager::HeadOperation) 18             && scheme == QLatin1String("data")) { 19         return new QNetworkReplyDataImpl(this, req, op); 20     } 21  22     // A request with QNetworkRequest::AlwaysCache does not need any bearer management 23     QNetworkRequest::CacheLoadControl mode = 24         static_cast
( 25 req.attribute(QNetworkRequest::CacheLoadControlAttribute, 26 QNetworkRequest::PreferNetwork).toInt()); 27 if (mode == QNetworkRequest::AlwaysCache 28 && (op == QNetworkAccessManager::GetOperation 29 || op == QNetworkAccessManager::HeadOperation)) { 30 // FIXME Implement a QNetworkReplyCacheImpl instead, see QTBUG-15106 31 QNetworkReplyImpl *reply = new QNetworkReplyImpl(this); 32 QNetworkReplyImplPrivate *priv = reply->d_func(); 33 priv->manager = this; 34 priv->backend = new QNetworkAccessCacheBackend(); 35 priv->backend->manager = this->d_func(); 36 priv->backend->setParent(reply); 37 priv->backend->reply = priv; 38 priv->setup(op, req, outgoingData); 39 return reply; 40 } 41 42 #ifndef QT_NO_BEARERMANAGEMENT 43 // Return a disabled network reply if network access is disabled. 44 // Except if the scheme is empty or file://. 45 if (!d->networkAccessible && !isLocalFile) { 46 return new QDisabledNetworkReply(this, req, op); 47 } 48 49 if (!d->networkSessionStrongRef && (d->initializeSession || !d->networkConfiguration.isEmpty())) { 50 QNetworkConfigurationManager manager; 51 if (!d->networkConfiguration.isEmpty()) { 52 d->createSession(manager.configurationFromIdentifier(d->networkConfiguration)); 53 } else { 54 if (manager.capabilities() & QNetworkConfigurationManager::NetworkSessionRequired) 55 d->createSession(manager.defaultConfiguration()); 56 else 57 d->initializeSession = false; 58 } 59 } 60 #endif 61 62 QNetworkRequest request = req; 63 if (!request.header(QNetworkRequest::ContentLengthHeader).isValid() && 64 outgoingData && !outgoingData->isSequential()) { 65 // request has no Content-Length 66 // but the data that is outgoing is random-access 67 request.setHeader(QNetworkRequest::ContentLengthHeader, outgoingData->size()); 68 } 69 70 if (static_cast
71 (request.attribute(QNetworkRequest::CookieLoadControlAttribute, 72 QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Automatic) { 73 if (d->cookieJar) { 74 QList
cookies = d->cookieJar->cookiesForUrl(request.url()); 75 if (!cookies.isEmpty()) 76 request.setHeader(QNetworkRequest::CookieHeader, QVariant::fromValue(cookies)); 77 } 78 } 79 80 // first step: create the reply 81 QUrl url = request.url(); 82 QNetworkReplyImpl *reply = new QNetworkReplyImpl(this); 83 #ifndef QT_NO_BEARERMANAGEMENT 84 if (!isLocalFile) { 85 connect(this, SIGNAL(networkSessionConnected()), 86 reply, SLOT(_q_networkSessionConnected())); 87 } 88 #endif 89 QNetworkReplyImplPrivate *priv = reply->d_func(); 90 priv->manager = this; 91 92 // second step: fetch cached credentials 93 // This is not done for the time being, we should use signal emissions to request 94 // the credentials from cache. 95 96 // third step: find a backend 97 priv->backend = d->findBackend(op, request); 98 99 if (priv->backend) {100 priv->backend->setParent(reply);101 priv->backend->reply = priv;102 }103 104 #ifndef QT_NO_OPENSSL105 reply->setSslConfiguration(request.sslConfiguration());106 #endif107 108 // fourth step: setup the reply109 priv->setup(op, request, outgoingData);110 111 return reply;112 }

代码比较长,主要做了这些事情:

1、设定HTTP请求的头信息(例如客户端请求内容的长度、Cookie等)

2、生成并初始化Reply对象(实际是QNetworkReplyImpl对象)

3、获取本地缓存的认证信息(如果有的话)

4、设定Reply

5、获取一个backend实体

6、如果支持OPENSSL的话,设定SSL的配置

暂时先放一边后面再对createRequest()做进一步的分析,再来看postProcess()

1 QNetworkReply *QNetworkAccessManagerPrivate::postProcess(QNetworkReply *reply) 2 { 3     Q_Q(QNetworkAccessManager); 4     QNetworkReplyPrivate::setManager(reply, q); 5     q->connect(reply, SIGNAL(finished()), SLOT(_q_replyFinished())); 6 #ifndef QT_NO_OPENSSL 7     /* In case we're compiled without SSL support, we don't have this signal and we need to 8      * avoid getting a connection error. */ 9     q->connect(reply, SIGNAL(sslErrors(QList
)), SLOT(_q_replySslErrors(QList
)));10 #endif11 #ifndef QT_NO_BEARERMANAGEMENT12 activeReplyCount++;13 #endif14 15 return reply;16 }

简单来说就做了一件事情,把QNetworkReply的信号(finished、sslErrors)与QNetworkAccessManager的槽连接起来

接上面,进一步分析QNetworkAccessManager::createRequest()的实现。去除不重要的分支末节,看其调用的QNetworkReplyImplPrivate::setup()和QNetworkAccessManagerPrivate::findBackend()的代码

1 void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const QNetworkRequest &req, 2                                      QIODevice *data) 3 { 4     Q_Q(QNetworkReplyImpl); 5  6     outgoingData = data;  //outgoingData实际就是QNetworkRequest对象 7     request = req; 8     url = request.url(); 9     operation = op;10 11     q->QIODevice::open(QIODevice::ReadOnly);12     // Internal code that does a HTTP reply for the synchronous Ajax13     // in QtWebKit.14     QVariant synchronousHttpAttribute = req.attribute(15             static_cast
(QNetworkRequest::SynchronousRequestAttribute));16 // The synchronous HTTP is a corner case, we will put all upload data in one big QByteArray in the outgoingDataBuffer.17 // Yes, this is not the most efficient thing to do, but on the other hand synchronous XHR needs to die anyway.18 if (synchronousHttpAttribute.toBool() && outgoingData) {19 outgoingDataBuffer = QSharedPointer
(new QRingBuffer());20 qint64 previousDataSize = 0;21 do {22 previousDataSize = outgoingDataBuffer->size();23 outgoingDataBuffer->append(outgoingData->readAll());24 } while (outgoingDataBuffer->size() != previousDataSize);25 }26 27 if (backend)28 backend->setSynchronous(synchronousHttpAttribute.toBool());29 30 31 if (outgoingData && backend && !backend->isSynchronous()) {32 // there is data to be uploaded, e.g. HTTP POST.33 34 if (!backend->needsResetableUploadData() || !outgoingData->isSequential()) {35 // backend does not need upload buffering or36 // fixed size non-sequential37 // just start the operation38 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);39 } else {40 bool bufferingDisallowed =41 req.attribute(QNetworkRequest::DoNotBufferUploadDataAttribute,42 false).toBool();43 44 if (bufferingDisallowed) {45 // if a valid content-length header for the request was supplied, we can disable buffering46 // if not, we will buffer anyway47 if (req.header(QNetworkRequest::ContentLengthHeader).isValid()) {48 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);49 } else {50 state = Buffering;51 QMetaObject::invokeMethod(q, "_q_bufferOutgoingData", Qt::QueuedConnection);52 }53 } else {54 // _q_startOperation will be called when the buffering has finished.55 state = Buffering;56 QMetaObject::invokeMethod(q, "_q_bufferOutgoingData", Qt::QueuedConnection);57 }58 }59 } else {60 // for HTTP, we want to send out the request as fast as possible to the network, without61 // invoking methods in a QueuedConnection62 #ifndef QT_NO_HTTP63 if (qobject_cast
(backend) || (backend && backend->isSynchronous())) {64 _q_startOperation();65 } else {66 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);67 }68 #else69 if (backend && backend->isSynchronous())70 _q_startOperation();71 else72 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);73 #endif // QT_NO_HTTP74 }75 }

发现调用_q_startOperation函数和_q_bufferOutgoingData函数,代码如下

1 void QNetworkReplyImplPrivate::_q_startOperation() 2 { 3     // ensure this function is only being called once 4     if (state == Working || state == Finished) { 5         qDebug("QNetworkReplyImpl::_q_startOperation was called more than once"); 6         return; 7     } 8     state = Working; 9 10     // note: if that method is called directly, it cannot happen that the backend is 0,11     // because we just checked via a qobject_cast that we got a http backend (see12     // QNetworkReplyImplPrivate::setup())13     if (!backend) {14         error(QNetworkReplyImpl::ProtocolUnknownError,15               QCoreApplication::translate("QNetworkReply", "Protocol \"%1\" is unknown").arg(url.scheme())); // not really true!;16         finished();17         return;18     }19 20     if (!backend->start()) {21 #ifndef QT_NO_BEARERMANAGEMENT22         // backend failed to start because the session state is not Connected.23         // QNetworkAccessManager will call _q_startOperation again for us when the session24         // state changes.25         state = WaitingForSession;26 27         QSharedPointer
session(manager->d_func()->getNetworkSession());28 29 if (session) {30 Q_Q(QNetworkReplyImpl);31 32 QObject::connect(session.data(), SIGNAL(error(QNetworkSession::SessionError)),33 q, SLOT(_q_networkSessionFailed()), Qt::QueuedConnection);34 35 if (!session->isOpen())36 session->open();37 } else {38 qWarning("Backend is waiting for QNetworkSession to connect, but there is none!");39 state = Working;40 error(QNetworkReplyImpl::UnknownNetworkError,41 QCoreApplication::translate("QNetworkReply", "Network session error."));42 finished();43 }44 #else45 qWarning("Backend start failed");46 state = Working;47 error(QNetworkReplyImpl::UnknownNetworkError,48 QCoreApplication::translate("QNetworkReply", "backend start error."));49 finished();50 #endif51 return;52 }53 54 if (backend && backend->isSynchronous()) {55 state = Finished;56 q_func()->setFinished(true);57 } else {58 if (state != Finished) {59 if (operation == QNetworkAccessManager::GetOperation)60 pendingNotifications.append(NotifyDownstreamReadyWrite);61 62 handleNotifications();63 }64 }65 }

 

1 void QNetworkReplyImplPrivate::_q_bufferOutgoingData() 2 { 3     Q_Q(QNetworkReplyImpl); 4  5     if (!outgoingDataBuffer) { 6         // first call, create our buffer 7         outgoingDataBuffer = QSharedPointer
(new QRingBuffer()); 8 9 QObject::connect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));10 QObject::connect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));11 }12 13 qint64 bytesBuffered = 0;14 qint64 bytesToBuffer = 0;15 16 // read data into our buffer17 forever {18 bytesToBuffer = outgoingData->bytesAvailable();19 // unknown? just try 2 kB, this also ensures we always try to read the EOF20 if (bytesToBuffer <= 0)21 bytesToBuffer = 2*1024;22 23 char *dst = outgoingDataBuffer->reserve(bytesToBuffer);24 bytesBuffered = outgoingData->read(dst, bytesToBuffer);25 26 if (bytesBuffered == -1) {27 // EOF has been reached.28 outgoingDataBuffer->chop(bytesToBuffer);29 30 _q_bufferOutgoingDataFinished();31 break;32 } else if (bytesBuffered == 0) {33 // nothing read right now, just wait until we get called again34 outgoingDataBuffer->chop(bytesToBuffer);35 36 break;37 } else {38 // don't break, try to read() again39 outgoingDataBuffer->chop(bytesToBuffer - bytesBuffered);40 }41 }42 }

连接两个信号与槽之后,是打开QIODevice,暂未深入分析。然后是呼叫q->_q_startOperation(),实际就是调用QNetworkReplyImpl::_q_startOperation(),使用的是队列等待方式(也就是发送一个消息进入系统消息队列,这个setup函数以及全部后续执行完毕,主动权交回给Windows后,再根据进入队列的消息来触发)。

_q_startOperation就是做了一些简单的判断,然后调用 handleNotifications

 

1 void QNetworkReplyImplPrivate::handleNotifications() 2 { 3     if (notificationHandlingPaused) 4         return; 5  6     NotificationQueue current = pendingNotifications; 7     pendingNotifications.clear(); 8  9     if (state != Working)10         return;11 12     while (state == Working && !current.isEmpty()) {13         InternalNotifications notification = current.dequeue();14         switch (notification) {15         case NotifyDownstreamReadyWrite:16             if (copyDevice)17                 _q_copyReadyRead();18             else19                 backend->downstreamReadyWrite();20             break;21 22         case NotifyCloseDownstreamChannel:23             backend->closeDownstreamChannel();24             break;25 26         case NotifyCopyFinished: {27             QIODevice *dev = copyDevice;28             copyDevice = 0;29             backend->copyFinished(dev);30             break;31         }32         }33     }34 }

该函数主要用于处理各种socket相关事件

因此我们先看QNetworkAccessManagerPrivate::findBackend()的代码实现

1 QNetworkAccessBackend *QNetworkAccessManagerPrivate::findBackend(QNetworkAccessManager::Operation op, 2                                                                  const QNetworkRequest &request) 3 { 4     if (QNetworkAccessBackendFactoryData::valid) { 5         QMutexLocker locker(&factoryData()->mutex); 6         QNetworkAccessBackendFactoryData::ConstIterator it = factoryData()->constBegin(), 7                                                            end = factoryData()->constEnd(); 8         while (it != end) { 9             QNetworkAccessBackend *backend = (*it)->create(op, request);10             if (backend) {11                 backend->manager = this;12                 return backend; // found a factory that handled our request13             }14             ++it;15         }16     }17     return 0;18 }

这段代码有一点复杂,先看红色标记的第一句,factoryData()是用宏来定义的函数:

1 Q_GLOBAL_STATIC(QNetworkAccessBackendFactoryData, factoryData)

宏定义如下:

1 #define Q_GLOBAL_STATIC(TYPE, NAME)                                  \2     static TYPE *NAME()                                              \3     {                                                                \4         static TYPE thisVariable;                                    \5         static QGlobalStatic
thisGlobalStatic(&thisVariable); \6 return thisGlobalStatic.pointer; \7 }

如果对STD比较熟悉,第一感觉这是一个模板List操作。在这里constBegin()和constEnd()组合起来是一个遍历,那么在什么地方设定值呢?良好代码的命名是很规范的,我试了试全局查找factoryData(),找到了我所希望看到的东西:

1 QNetworkAccessBackendFactory::QNetworkAccessBackendFactory() 2 { 3     QMutexLocker locker(&factoryData()->mutex); 4     factoryData()->append(this); 5 } 6  7 QNetworkAccessBackendFactory::~QNetworkAccessBackendFactory() 8 { 9     if (QNetworkAccessBackendFactoryData::valid) {10         QMutexLocker locker(&factoryData()->mutex);11         factoryData()->removeAll(this);12     }13 }

里prepend()应该是把对象添加到列表;而removeAll()就是清空全部数据了。

factoryData()里面包含的对象序列,应该是从QNetworkAccessBackendFactory衍生出来的。
一共有哪些子类呢?继续全局查找

1 class QNetworkAccessDataBackendFactory: public QNetworkAccessBackendFactory2 class QNetworkAccessDebugPipeBackendFactory: public QNetworkAccessBackendFactory3 class QNetworkAccessFileBackendFactory: public QNetworkAccessBackendFactory4 class QNetworkAccessFtpBackendFactory: public QNetworkAccessBackendFactory5 class QNetworkAccessHttpBackendFactory : public QNetworkAccessBackendFactory

去除暂时不关心的DebugPipe,一共有四种:DataBackend、FileBackend、FtpBackend、HttpBackend。媒体的种类原来是在这里实现的。看其中QNetworkAccessHttpBackendFactory::create()

 

1 QNetworkAccessBackend * 2 QNetworkAccessHttpBackendFactory::create(QNetworkAccessManager::Operation op, 3                                          const QNetworkRequest &request) const 4 { 5     // check the operation 6     switch (op) { 7     case QNetworkAccessManager::GetOperation: 8     case QNetworkAccessManager::PostOperation: 9     case QNetworkAccessManager::HeadOperation:10     case QNetworkAccessManager::PutOperation:11     case QNetworkAccessManager::DeleteOperation:12     case QNetworkAccessManager::CustomOperation:13         break;14 15     default:16         // no, we can't handle this request17         return 0;18     }19 20     QUrl url = request.url();21     QString scheme = url.scheme().toLower();22     if (scheme == QLatin1String("http") || scheme == QLatin1String("https"))23         return new QNetworkAccessHttpBackend;24 25     return 0;26 }

 

如果是能够处理的OP标记并且URL的前缀是http或者是https,则创建一个QNetworkAccessHttpBackend对象。

前面QNetworkAccessManager::get()代码中,调用的参数是QNetworkAccessManager::GetOperation,所以在我们分析的这个应用中,创建的是QNetworkAccessHttpBackend对象。
findBackend()到此分析完毕;由于factoryData()的具体实现跟我们分析网络通信的目标没有太大关系,未深入分析,有谁分析了的话请转告一声,值得一看。

回到前面暂停的QNetworkReplyImpl::_q_startOperation(),又实现了什么动作呢?

首先调用了刚刚创建的QNetworkAccessHttpBackend::start(),然后是添加通知消息、调用_q_sourceReadyRead()、最后处理通知消息

1 bool QNetworkAccessBackend::start() 2 { 3 #ifndef QT_NO_BEARERMANAGEMENT 4     // For bearer, check if session start is required 5     QSharedPointer
networkSession(manager->getNetworkSession()); 6 if (networkSession) { 7 // session required 8 if (networkSession->isOpen() && 9 networkSession->state() == QNetworkSession::Connected) {10 // Session is already open and ready to use.11 // copy network session down to the backend12 setProperty("_q_networksession", QVariant::fromValue(networkSession));13 } else {14 // Session not ready, but can skip for loopback connections15 16 // This is not ideal.17 const QString host = reply->url.host();18 19 if (host == QLatin1String("localhost") ||20 QHostAddress(host) == QHostAddress::LocalHost ||21 QHostAddress(host) == QHostAddress::LocalHostIPv6) {22 // Don't need an open session for localhost access.23 } else {24 // need to wait for session to be opened25 return false;26 }27 }28 }29 #endif30 31 #ifndef QT_NO_NETWORKPROXY32 #ifndef QT_NO_BEARERMANAGEMENT33 // Get the proxy settings from the network session (in the case of service networks,34 // the proxy settings change depending which AP was activated)35 QNetworkSession *session = networkSession.data();36 QNetworkConfiguration config;37 if (session) {38 QNetworkConfigurationManager configManager;39 // The active configuration tells us what IAP is in use40 QVariant v = session->sessionProperty(QLatin1String("ActiveConfiguration"));41 if (v.isValid())42 config = configManager.configurationFromIdentifier(qvariant_cast
(v));43 // Fallback to using the configuration if no active configuration44 if (!config.isValid())45 config = session->configuration();46 // or unspecified configuration if that is no good either47 if (!config.isValid())48 config = QNetworkConfiguration();49 }50 reply->proxyList = manager->queryProxy(QNetworkProxyQuery(config, url()));51 #else // QT_NO_BEARERMANAGEMENT52 // Without bearer management, the proxy depends only on the url53 reply->proxyList = manager->queryProxy(QNetworkProxyQuery(url()));54 #endif55 #endif56 57 // now start the request58 open();59 return true;60 }

start函数很简单,主要是打开QNetworkAccessBackend

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

1 void QNetworkAccessHttpBackend::open()2 {3     postRequest();4 }

open函数仅仅是调用postRequest()

1 etworkAccessHttpBackend::postRequest()  2 {  3     QThread *thread = 0;  4     if (isSynchronous()) {  5         // A synchronous HTTP request uses its own thread  6         thread = new QThread();  7         QObject::connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));  8         thread->start();  9     } else if (!manager->httpThread) { 10         // We use the manager-global thread. 11         // At some point we could switch to having multiple threads if it makes sense. 12         manager->httpThread = new QThread(); 13         QObject::connect(manager->httpThread, SIGNAL(finished()), manager->httpThread, SLOT(deleteLater())); 14         manager->httpThread->start(); 15 #ifndef QT_NO_NETWORKPROXY 16         qRegisterMetaType
("QNetworkProxy"); 17 #endif 18 #ifndef QT_NO_OPENSSL 19 qRegisterMetaType
>("QList
"); 20 qRegisterMetaType
("QSslConfiguration"); 21 #endif 22 qRegisterMetaType
> >("QList
>"); 23 qRegisterMetaType
("QHttpNetworkRequest"); 24 qRegisterMetaType
("QNetworkReply::NetworkError"); 25 qRegisterMetaType
>("QSharedPointer
"); 26 27 thread = manager->httpThread; 28 } else { 29 // Asynchronous request, thread already exists 30 thread = manager->httpThread; 31 } 32 33 QUrl url = request().url(); 34 httpRequest.setUrl(url); 35 36 bool ssl = url.scheme().toLower() == QLatin1String("https"); 37 setAttribute(QNetworkRequest::ConnectionEncryptedAttribute, ssl); 38 httpRequest.setSsl(ssl); 39 40 41 #ifndef QT_NO_NETWORKPROXY 42 QNetworkProxy transparentProxy, cacheProxy; 43 44 foreach (const QNetworkProxy &p, proxyList()) { 45 // use the first proxy that works 46 // for non-encrypted connections, any transparent or HTTP proxy 47 // for encrypted, only transparent proxies 48 if (!ssl 49 && (p.capabilities() & QNetworkProxy::CachingCapability) 50 && (p.type() == QNetworkProxy::HttpProxy || 51 p.type() == QNetworkProxy::HttpCachingProxy)) { 52 cacheProxy = p; 53 transparentProxy = QNetworkProxy::NoProxy; 54 break; 55 } 56 if (p.isTransparentProxy()) { 57 transparentProxy = p; 58 cacheProxy = QNetworkProxy::NoProxy; 59 break; 60 } 61 } 62 63 // check if at least one of the proxies 64 if (transparentProxy.type() == QNetworkProxy::DefaultProxy && 65 cacheProxy.type() == QNetworkProxy::DefaultProxy) { 66 // unsuitable proxies 67 QMetaObject::invokeMethod(this, "error", isSynchronous() ? Qt::DirectConnection : Qt::QueuedConnection, 68 Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ProxyNotFoundError), 69 Q_ARG(QString, tr("No suitable proxy found"))); 70 QMetaObject::invokeMethod(this, "finished", isSynchronous() ? Qt::DirectConnection : Qt::QueuedConnection); 71 return; 72 } 73 #endif 74 75 76 bool loadedFromCache = false; 77 httpRequest.setPriority(convert(request().priority())); 78 79 switch (operation()) { 80 case QNetworkAccessManager::GetOperation: 81 httpRequest.setOperation(QHttpNetworkRequest::Get); 82 loadedFromCache = loadFromCacheIfAllowed(httpRequest); 83 break; 84 85 case QNetworkAccessManager::HeadOperation: 86 httpRequest.setOperation(QHttpNetworkRequest::Head); 87 loadedFromCache = loadFromCacheIfAllowed(httpRequest); 88 break; 89 90 case QNetworkAccessManager::PostOperation: 91 invalidateCache(); 92 httpRequest.setOperation(QHttpNetworkRequest::Post); 93 createUploadByteDevice(); 94 break; 95 96 case QNetworkAccessManager::PutOperation: 97 invalidateCache(); 98 httpRequest.setOperation(QHttpNetworkRequest::Put); 99 createUploadByteDevice();100 break;101 102 case QNetworkAccessManager::DeleteOperation:103 invalidateCache();104 httpRequest.setOperation(QHttpNetworkRequest::Delete);105 break;106 107 case QNetworkAccessManager::CustomOperation:108 invalidateCache(); // for safety reasons, we don't know what the operation does109 httpRequest.setOperation(QHttpNetworkRequest::Custom);110 createUploadByteDevice();111 httpRequest.setCustomVerb(request().attribute(112 QNetworkRequest::CustomVerbAttribute).toByteArray());113 break;114 115 default:116 break; // can't happen117 }118 119 if (loadedFromCache) {120 // commented this out since it will be called later anyway121 // by copyFinished()122 //QNetworkAccessBackend::finished();123 return; // no need to send the request! :)124 }125 126 QList
headers = request().rawHeaderList();127 if (resumeOffset != 0) {128 if (headers.contains("Range")) {129 // Need to adjust resume offset for user specified range130 131 headers.removeOne("Range");132 133 // We've already verified that requestRange starts with "bytes=", see canResume.134 QByteArray requestRange = request().rawHeader("Range").mid(6);135 136 int index = requestRange.indexOf('-');137 138 quint64 requestStartOffset = requestRange.left(index).toULongLong();139 quint64 requestEndOffset = requestRange.mid(index + 1).toULongLong();140 141 requestRange = "bytes=" + QByteArray::number(resumeOffset + requestStartOffset) +142 '-' + QByteArray::number(requestEndOffset);143 144 httpRequest.setHeaderField("Range", requestRange);145 } else {146 httpRequest.setHeaderField("Range", "bytes=" + QByteArray::number(resumeOffset) + '-');147 }148 }149 150 foreach (const QByteArray &header, headers)151 httpRequest.setHeaderField(header, request().rawHeader(header));152 153 if (request().attribute(QNetworkRequest::HttpPipeliningAllowedAttribute).toBool() == true)154 httpRequest.setPipeliningAllowed(true);155 156 if (static_cast
157 (request().attribute(QNetworkRequest::AuthenticationReuseAttribute,158 QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Manual)159 httpRequest.setWithCredentials(false);160 161 162 // Create the HTTP thread delegate163 QHttpThreadDelegate *delegate = new QHttpThreadDelegate;164 #ifndef QT_NO_BEARERMANAGEMENT165 QVariant v(property("_q_networksession"));166 if (v.isValid())167 delegate->networkSession = qvariant_cast
>(v);168 #endif169 170 // For the synchronous HTTP, this is the normal way the delegate gets deleted171 // For the asynchronous HTTP this is a safety measure, the delegate deletes itself when HTTP is finished172 connect(thread, SIGNAL(finished()), delegate, SLOT(deleteLater()));173 174 // Set the properties it needs175 delegate->httpRequest = httpRequest;176 #ifndef QT_NO_NETWORKPROXY177 delegate->cacheProxy = cacheProxy;178 delegate->transparentProxy = transparentProxy;179 #endif180 delegate->ssl = ssl;181 #ifndef QT_NO_OPENSSL182 if (ssl)183 delegate->incomingSslConfiguration = request().sslConfiguration();184 #endif185 186 // Do we use synchronous HTTP?187 delegate->synchronous = isSynchronous();188 189 // The authentication manager is used to avoid the BlockingQueuedConnection communication190 // from HTTP thread to user thread in some cases.191 delegate->authenticationManager = manager->authenticationManager;192 193 if (!isSynchronous()) {194 // Tell our zerocopy policy to the delegate195 delegate->downloadBufferMaximumSize =196 request().attribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute).toLongLong();197 198 // These atomic integers are used for signal compression199 delegate->pendingDownloadData = pendingDownloadDataEmissions;200 delegate->pendingDownloadProgress = pendingDownloadProgressEmissions;201 202 // Connect the signals of the delegate to us203 connect(delegate, SIGNAL(downloadData(QByteArray)),204 this, SLOT(replyDownloadData(QByteArray)),205 Qt::QueuedConnection);206 connect(delegate, SIGNAL(downloadFinished()),207 this, SLOT(replyFinished()),208 Qt::QueuedConnection);209 connect(delegate, SIGNAL(downloadMetaData(QList
>,int,QString,bool,QSharedPointer
,qint64)),210 this, SLOT(replyDownloadMetaData(QList
>,int,QString,bool,QSharedPointer
,qint64)),211 Qt::QueuedConnection);212 connect(delegate, SIGNAL(downloadProgress(qint64,qint64)),213 this, SLOT(replyDownloadProgressSlot(qint64,qint64)),214 Qt::QueuedConnection);215 connect(delegate, SIGNAL(error(QNetworkReply::NetworkError,QString)),216 this, SLOT(httpError(QNetworkReply::NetworkError, const QString)),217 Qt::QueuedConnection);218 #ifndef QT_NO_OPENSSL219 connect(delegate, SIGNAL(sslConfigurationChanged(QSslConfiguration)),220 this, SLOT(replySslConfigurationChanged(QSslConfiguration)),221 Qt::QueuedConnection);222 #endif223 // Those need to report back, therefire BlockingQueuedConnection224 connect(delegate, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)),225 this, SLOT(httpAuthenticationRequired(QHttpNetworkRequest,QAuthenticator*)),226 Qt::BlockingQueuedConnection);227 #ifndef QT_NO_NETWORKPROXY228 connect (delegate, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),229 this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),230 Qt::BlockingQueuedConnection);231 #endif232 #ifndef QT_NO_OPENSSL233 connect(delegate, SIGNAL(sslErrors(QList
,bool*,QList
*)),234 this, SLOT(replySslErrors(const QList
&, bool *, QList
*)),235 Qt::BlockingQueuedConnection);236 #endif237 // This signal we will use to start the request.238 connect(this, SIGNAL(startHttpRequest()), delegate, SLOT(startRequest()));239 connect(this, SIGNAL(abortHttpRequest()), delegate, SLOT(abortRequest()));240 241 // To throttle the connection.242 QObject::connect(this, SIGNAL(readBufferSizeChanged(qint64)), delegate, SLOT(readBufferSizeChanged(qint64)));243 QObject::connect(this, SIGNAL(readBufferFreed(qint64)), delegate, SLOT(readBufferFreed(qint64)));244 245 if (uploadByteDevice) {246 QNonContiguousByteDeviceThreadForwardImpl *forwardUploadDevice =247 new QNonContiguousByteDeviceThreadForwardImpl(uploadByteDevice->atEnd(), uploadByteDevice->size());248 if (uploadByteDevice->isResetDisabled())249 forwardUploadDevice->disableReset();250 forwardUploadDevice->setParent(delegate); // needed to make sure it is moved on moveToThread()251 delegate->httpRequest.setUploadByteDevice(forwardUploadDevice);252 253 // From main thread to user thread:254 QObject::connect(this, SIGNAL(haveUploadData(QByteArray, bool, qint64)),255 forwardUploadDevice, SLOT(haveDataSlot(QByteArray, bool, qint64)), Qt::QueuedConnection);256 QObject::connect(uploadByteDevice.data(), SIGNAL(readyRead()),257 forwardUploadDevice, SIGNAL(readyRead()),258 Qt::QueuedConnection);259 260 // From http thread to user thread:261 QObject::connect(forwardUploadDevice, SIGNAL(wantData(qint64)),262 this, SLOT(wantUploadDataSlot(qint64)));263 QObject::connect(forwardUploadDevice, SIGNAL(processedData(qint64)),264 this, SLOT(sentUploadDataSlot(qint64)));265 connect(forwardUploadDevice, SIGNAL(resetData(bool*)),266 this, SLOT(resetUploadDataSlot(bool*)),267 Qt::BlockingQueuedConnection); // this is the only one with BlockingQueued!268 }269 } else if (isSynchronous()) {270 connect(this, SIGNAL(startHttpRequestSynchronously()), delegate, SLOT(startRequestSynchronously()), Qt::BlockingQueuedConnection);271 272 if (uploadByteDevice) {273 // For the synchronous HTTP use case the use thread (this one here) is blocked274 // so we cannot use the asynchronous upload architecture.275 // We therefore won't use the QNonContiguousByteDeviceThreadForwardImpl but directly276 // use the uploadByteDevice provided to us by the QNetworkReplyImpl.277 // The code that is in QNetworkReplyImplPrivate::setup() makes sure it is safe to use from a thread278 // since it only wraps a QRingBuffer279 delegate->httpRequest.setUploadByteDevice(uploadByteDevice.data());280 }281 }282 283 284 // Move the delegate to the http thread285 delegate->moveToThread(thread);286 // This call automatically moves the uploadDevice too for the asynchronous case.287 288 // Send an signal to the delegate so it starts working in the other thread289 if (isSynchronous()) {290 emit startHttpRequestSynchronously(); // This one is BlockingQueuedConnection, so it will return when all work is done291 292 if (delegate->incomingErrorCode != QNetworkReply::NoError) {293 replyDownloadMetaData294 (delegate->incomingHeaders,295 delegate->incomingStatusCode,296 delegate->incomingReasonPhrase,297 delegate->isPipeliningUsed,298 QSharedPointer
(),299 delegate->incomingContentLength);300 replyDownloadData(delegate->synchronousDownloadData);301 httpError(delegate->incomingErrorCode, delegate->incomingErrorDetail);302 } else {303 replyDownloadMetaData304 (delegate->incomingHeaders,305 delegate->incomingStatusCode,306 delegate->incomingReasonPhrase,307 delegate->isPipeliningUsed,308 QSharedPointer
(),309 delegate->incomingContentLength);310 replyDownloadData(delegate->synchronousDownloadData);311 }312 313 // End the thread. It will delete itself from the finished() signal314 thread->quit();315 thread->wait(5000);316 317 finished();318 } else {319 emit startHttpRequest(); // Signal to the HTTP thread and go back to user.320 }321 }

主要是链接槽函数,看槽函数代码startRequest

1 void QHttpThreadDelegate::startRequest() 2 { 3 #ifdef QHTTPTHREADDELEGATE_DEBUG 4     qDebug() << "QHttpThreadDelegate::startRequest() thread=" << QThread::currentThreadId(); 5 #endif 6     // Check QThreadStorage for the QNetworkAccessCache 7     // If not there, create this connection cache 8     if (!connections.hasLocalData()) { 9         connections.setLocalData(new QNetworkAccessCache());10     }11 12     // check if we have an open connection to this host13     QUrl urlCopy = httpRequest.url();14     urlCopy.setPort(urlCopy.port(ssl ? 443 : 80));15 16 #ifndef QT_NO_NETWORKPROXY17     if (transparentProxy.type() != QNetworkProxy::NoProxy)18         cacheKey = makeCacheKey(urlCopy, &transparentProxy);19     else if (cacheProxy.type() != QNetworkProxy::NoProxy)20         cacheKey = makeCacheKey(urlCopy, &cacheProxy);21     else22 #endif23         cacheKey = makeCacheKey(urlCopy, 0);24 25 26     // the http object is actually a QHttpNetworkConnection27     httpConnection = static_cast
(connections.localData()->requestEntryNow(cacheKey));28 if (httpConnection == 0) {29 // no entry in cache; create an object30 // the http object is actually a QHttpNetworkConnection31 #ifdef QT_NO_BEARERMANAGEMENT32 httpConnection = new QNetworkAccessCachedHttpConnection(urlCopy.host(), urlCopy.port(), ssl);33 #else34 httpConnection = new QNetworkAccessCachedHttpConnection(urlCopy.host(), urlCopy.port(), ssl, networkSession);35 #endif36 #ifndef QT_NO_OPENSSL37 // Set the QSslConfiguration from this QNetworkRequest.38 if (ssl && incomingSslConfiguration != QSslConfiguration::defaultConfiguration()) {39 httpConnection->setSslConfiguration(incomingSslConfiguration);40 }41 #endif42 43 #ifndef QT_NO_NETWORKPROXY44 httpConnection->setTransparentProxy(transparentProxy);45 httpConnection->setCacheProxy(cacheProxy);46 #endif47 48 // cache the QHttpNetworkConnection corresponding to this cache key49 connections.localData()->addEntry(cacheKey, httpConnection);50 }51 52 53 // Send the request to the connection54 httpReply = httpConnection->sendRequest(httpRequest);55 httpReply->setParent(this);56 57 // Connect the reply signals that we need to handle and then forward58 if (synchronous) {59 connect(httpReply,SIGNAL(headerChanged()), this, SLOT(synchronousHeaderChangedSlot()));60 connect(httpReply,SIGNAL(finished()), this, SLOT(synchronousFinishedSlot()));61 connect(httpReply,SIGNAL(finishedWithError(QNetworkReply::NetworkError, const QString)),62 this, SLOT(synchronousFinishedWithErrorSlot(QNetworkReply::NetworkError,QString)));63 64 connect(httpReply, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)),65 this, SLOT(synchronousAuthenticationRequiredSlot(QHttpNetworkRequest,QAuthenticator*)));66 connect(httpReply, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),67 this, SLOT(synchronousProxyAuthenticationRequiredSlot(QNetworkProxy,QAuthenticator*)));68 69 // Don't care about ignored SSL errors for now in the synchronous HTTP case.70 } else if (!synchronous) {71 connect(httpReply,SIGNAL(headerChanged()), this, SLOT(headerChangedSlot()));72 connect(httpReply,SIGNAL(finished()), this, SLOT(finishedSlot()));73 connect(httpReply,SIGNAL(finishedWithError(QNetworkReply::NetworkError, const QString)),74 this, SLOT(finishedWithErrorSlot(QNetworkReply::NetworkError,QString)));75 // some signals are only interesting when normal asynchronous style is used76 connect(httpReply,SIGNAL(readyRead()), this, SLOT(readyReadSlot()));77 connect(httpReply,SIGNAL(dataReadProgress(int, int)), this, SLOT(dataReadProgressSlot(int,int)));78 #ifndef QT_NO_OPENSSL79 connect(httpReply,SIGNAL(sslErrors(const QList
)), this, SLOT(sslErrorsSlot(QList
)));80 #endif81 82 // In the asynchronous HTTP case we can just forward those signals83 // Connect the reply signals that we can directly forward84 connect(httpReply, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)),85 this, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)));86 connect(httpReply, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),87 this, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));88 }89 90 connect(httpReply, SIGNAL(cacheCredentials(QHttpNetworkRequest,QAuthenticator*)),91 this, SLOT(cacheCredentialsSlot(QHttpNetworkRequest,QAuthenticator*)));92 }

先查缓冲,没用的话新建连接,然后调用其sendRequest

1 QHttpNetworkReply* QHttpNetworkConnection::sendRequest(const QHttpNetworkRequest &request)2 {3     Q_D(QHttpNetworkConnection);4     return d->queueRequest(request);5 }

sendRequest()调用queueRequest()函数

1 QHttpNetworkReply* QHttpNetworkConnectionPrivate::queueRequest(const QHttpNetworkRequest &request) 2 { 3     Q_Q(QHttpNetworkConnection); 4  5     // The reply component of the pair is created initially. 6     QHttpNetworkReply *reply = new QHttpNetworkReply(request.url()); 7     reply->setRequest(request); 8     reply->d_func()->connection = q; 9     reply->d_func()->connectionChannel = &channels[0]; // will have the correct one set later10     HttpMessagePair pair = qMakePair(request, reply);11 12     switch (request.priority()) {13     case QHttpNetworkRequest::HighPriority:14         highPriorityQueue.prepend(pair);15         break;16     case QHttpNetworkRequest::NormalPriority:17     case QHttpNetworkRequest::LowPriority:18         lowPriorityQueue.prepend(pair);19         break;20     }21 22     // this used to be called via invokeMethod and a QueuedConnection23     // It is the only place _q_startNextRequest is called directly without going24     // through the event loop using a QueuedConnection.25     // This is dangerous because of recursion that might occur when emitting26     // signals as DirectConnection from this code path. Therefore all signal27     // emissions that can come out from this code path need to28     // be QueuedConnection.29     // We are currently trying to fine-tune this.30     _q_startNextRequest();31 32 33     return reply;34 }

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

循环多通道处理请求,类似于connect流程

1 void QHttpNetworkConnectionPrivate::_q_startNextRequest() 2 { 3     // If the QHttpNetworkConnection is currently paused then bail out immediately 4     if (state == PausedState) 5         return; 6  7     //resend the necessary ones. 8     for (int i = 0; i < channelCount; ++i) { 9         if (channels[i].resendCurrent && (channels[i].state != QHttpNetworkConnectionChannel::ClosingState)) {10             channels[i].resendCurrent = false;11             channels[i].state = QHttpNetworkConnectionChannel::IdleState;12 13             // if this is not possible, error will be emitted and connection terminated14             if (!channels[i].resetUploadData())15                 continue;16             channels[i].sendRequest();17         }18     }19 20     // dequeue new ones21 22     // return fast if there is nothing to do23     if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty())24         return;25     // try to get a free AND connected socket26     for (int i = 0; i < channelCount; ++i) {27         if (!channels[i].reply && !channels[i].isSocketBusy() && channels[i].socket->state() == QAbstractSocket::ConnectedState) {28             if (dequeueRequest(channels[i].socket))29                 channels[i].sendRequest();30         }31     }32 33     // try to push more into all sockets34     // ### FIXME we should move this to the beginning of the function35     // as soon as QtWebkit is properly using the pipelining36     // (e.g. not for XMLHttpRequest or the first page load)37     // ### FIXME we should also divide the requests more even38     // on the connected sockets39     //tryToFillPipeline(socket);40     // return fast if there is nothing to pipeline41     if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty())42         return;43     for (int i = 0; i < channelCount; i++)44         if (channels[i].socket->state() == QAbstractSocket::ConnectedState)45             fillPipeline(channels[i].socket);46 47     // If there is not already any connected channels we need to connect a new one.48     // We do not pair the channel with the request until we know if it is 49     // connected or not. This is to reuse connected channels before we connect new once.50     int queuedRequest = highPriorityQueue.count() + lowPriorityQueue.count();51     for (int i = 0; i < channelCount; ++i) {52         if (channels[i].socket->state() == QAbstractSocket::ConnectingState)53             queuedRequest--;54         if ( queuedRequest <=0 )55             break;56         if (!channels[i].reply && !channels[i].isSocketBusy() && (channels[i].socket->state() == QAbstractSocket::UnconnectedState)) {57             channels[i].ensureConnection();58             queuedRequest--;59         }60     }61 }

 接着调用看代码

1 bool QHttpNetworkConnectionPrivate::dequeueRequest(QAbstractSocket *socket) 2 { 3     Q_ASSERT(socket); 4  5     int i = indexOf(socket); 6  7     if (!highPriorityQueue.isEmpty()) { 8         // remove from queue before sendRequest! else we might pipeline the same request again 9         HttpMessagePair messagePair = highPriorityQueue.takeLast();10         if (!messagePair.second->d_func()->requestIsPrepared)11             prepareRequest(messagePair);12         channels[i].request = messagePair.first;13         channels[i].reply = messagePair.second;14         return true;15     }16 17     if (!lowPriorityQueue.isEmpty()) {18         // remove from queue before sendRequest! else we might pipeline the same request again19         HttpMessagePair messagePair = lowPriorityQueue.takeLast();20         if (!messagePair.second->d_func()->requestIsPrepared)21             prepareRequest(messagePair);22         channels[i].request = messagePair.first;23         channels[i].reply = messagePair.second;24         return true;25     }26     return false;27 }

看看prepareReuest

1 void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair)  2 {  3     QHttpNetworkRequest &request = messagePair.first;  4     QHttpNetworkReply *reply = messagePair.second;  5   6     // add missing fields for the request  7     QByteArray value;  8     // check if Content-Length is provided  9     QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice(); 10     if (uploadByteDevice) { 11         if (request.contentLength() != -1 && uploadByteDevice->size() != -1) { 12             // both values known, take the smaller one. 13             request.setContentLength(qMin(uploadByteDevice->size(), request.contentLength())); 14         } else if (request.contentLength() == -1 && uploadByteDevice->size() != -1) { 15             // content length not supplied by user, but the upload device knows it 16             request.setContentLength(uploadByteDevice->size()); 17         } else if (request.contentLength() != -1 && uploadByteDevice->size() == -1) { 18             // everything OK, the user supplied us the contentLength 19         } else if (request.contentLength() == -1 && uploadByteDevice->size() == -1) { 20             qFatal("QHttpNetworkConnectionPrivate: Neither content-length nor upload device size were given"); 21         } 22     } 23     // set the Connection/Proxy-Connection: Keep-Alive headers 24 #ifndef QT_NO_NETWORKPROXY 25     if (networkProxy.type() == QNetworkProxy::HttpCachingProxy)  { 26         value = request.headerField("proxy-connection"); 27         if (value.isEmpty()) 28             request.setHeaderField("Proxy-Connection", "Keep-Alive"); 29     } else { 30 #endif 31         value = request.headerField("connection"); 32         if (value.isEmpty()) 33             request.setHeaderField("Connection", "Keep-Alive"); 34 #ifndef QT_NO_NETWORKPROXY 35     } 36 #endif 37  38     // If the request had a accept-encoding set, we better not mess 39     // with it. If it was not set, we announce that we understand gzip 40     // and remember this fact in request.d->autoDecompress so that 41     // we can later decompress the HTTP reply if it has such an 42     // encoding. 43     value = request.headerField("accept-encoding"); 44     if (value.isEmpty()) { 45 #ifndef QT_NO_COMPRESS 46         request.setHeaderField("Accept-Encoding", "gzip"); 47         request.d->autoDecompress = true; 48 #else 49         // if zlib is not available set this to false always 50         request.d->autoDecompress = false; 51 #endif 52     } 53  54     // some websites mandate an accept-language header and fail 55     // if it is not sent. This is a problem with the website and 56     // not with us, but we work around this by setting 57     // one always. 58     value = request.headerField("accept-language"); 59     if (value.isEmpty()) { 60         QString systemLocale = QLocale::system().name().replace(QChar::fromAscii('_'),QChar::fromAscii('-')); 61         QString acceptLanguage; 62         if (systemLocale == QLatin1String("C")) 63             acceptLanguage = QString::fromAscii("en,*"); 64         else if (systemLocale.startsWith(QLatin1String("en-"))) 65             acceptLanguage = QString::fromAscii("%1,*").arg(systemLocale); 66         else 67             acceptLanguage = QString::fromAscii("%1,en,*").arg(systemLocale); 68         request.setHeaderField("Accept-Language", acceptLanguage.toAscii()); 69     } 70  71     // set the User Agent 72     value = request.headerField("user-agent"); 73     if (value.isEmpty()) 74         request.setHeaderField("User-Agent", "Mozilla/5.0"); 75     // set the host 76     value = request.headerField("host"); 77     if (value.isEmpty()) { 78         QHostAddress add; 79         QByteArray host; 80         if(add.setAddress(hostName)) { 81             if(add.protocol() == QAbstractSocket::IPv6Protocol) { 82                 host = "[" + hostName.toAscii() + "]";//format the ipv6 in the standard way 83             } else { 84                 host = QUrl::toAce(hostName); 85             } 86         } else { 87             host = QUrl::toAce(hostName); 88         } 89  90         int port = request.url().port(); 91         if (port != -1) { 92             host += ':'; 93             host += QByteArray::number(port); 94         } 95  96         request.setHeaderField("Host", host); 97     } 98  99     reply->d_func()->requestIsPrepared = true;100 }

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

1 bool QHttpNetworkConnectionChannel::sendRequest()  2 {  3     if (!reply) {  4         // heh, how should that happen!  5         qWarning() << "QHttpNetworkConnectionChannel::sendRequest() called without QHttpNetworkReply";  6         state = QHttpNetworkConnectionChannel::IdleState;  7         return false;  8     }  9  10     switch (state) { 11     case QHttpNetworkConnectionChannel::IdleState: { // write the header 12         if (!ensureConnection()) { 13             // wait for the connection (and encryption) to be done 14             // sendRequest will be called again from either 15             // _q_connected or _q_encrypted 16             return false; 17         } 18         written = 0; // excluding the header 19         bytesTotal = 0; 20  21         QHttpNetworkReplyPrivate *replyPrivate = reply->d_func(); 22         replyPrivate->clear(); 23         replyPrivate->connection = connection; 24         replyPrivate->connectionChannel = this; 25         replyPrivate->autoDecompress = request.d->autoDecompress; 26         replyPrivate->pipeliningUsed = false; 27  28         // if the url contains authentication parameters, use the new ones 29         // both channels will use the new authentication parameters 30         if (!request.url().userInfo().isEmpty() && request.withCredentials()) { 31             QUrl url = request.url(); 32             QAuthenticator &auth = authenticator; 33             if (url.userName() != auth.user() 34                 || (!url.password().isEmpty() && url.password() != auth.password())) { 35                 auth.setUser(url.userName()); 36                 auth.setPassword(url.password()); 37                 connection->d_func()->copyCredentials(connection->d_func()->indexOf(socket), &auth, false); 38             } 39             // clear the userinfo,  since we use the same request for resending 40             // userinfo in url can conflict with the one in the authenticator 41             url.setUserInfo(QString()); 42             request.setUrl(url); 43         } 44         // Will only be false if QtWebKit is performing a cross-origin XMLHttpRequest 45         // and withCredentials has not been set to true. 46         if (request.withCredentials()) 47             connection->d_func()->createAuthorization(socket, request); 48 #ifndef QT_NO_NETWORKPROXY 49         QByteArray header = QHttpNetworkRequestPrivate::header(request, 50             (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy)); 51 #else 52         QByteArray header = QHttpNetworkRequestPrivate::header(request, false); 53 #endif 54         socket->write(header); 55         // flushing is dangerous (QSslSocket calls transmit which might read or error) 56 //        socket->flush(); 57         QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice(); 58         if (uploadByteDevice) { 59             // connect the signals so this function gets called again 60             QObject::connect(uploadByteDevice, SIGNAL(readyRead()),this, SLOT(_q_uploadDataReadyRead())); 61  62             bytesTotal = request.contentLength(); 63  64             state = QHttpNetworkConnectionChannel::WritingState; // start writing data 65             sendRequest(); //recurse 66         } else { 67             state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response 68             sendRequest(); //recurse 69         } 70  71         break; 72     } 73     case QHttpNetworkConnectionChannel::WritingState: 74     { 75         // write the data 76         QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice(); 77         if (!uploadByteDevice || bytesTotal == written) { 78             if (uploadByteDevice) 79                 emit reply->dataSendProgress(written, bytesTotal); 80             state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response 81             sendRequest(); // recurse 82             break; 83         } 84  85         // only feed the QTcpSocket buffer when there is less than 32 kB in it 86         const qint64 socketBufferFill = 32*1024; 87         const qint64 socketWriteMaxSize = 16*1024; 88  89  90 #ifndef QT_NO_OPENSSL 91         QSslSocket *sslSocket = qobject_cast
(socket); 92 // if it is really an ssl socket, check more than just bytesToWrite() 93 while ((socket->bytesToWrite() + (sslSocket ? sslSocket->encryptedBytesToWrite() : 0)) 94 <= socketBufferFill && bytesTotal != written) 95 #else 96 while (socket->bytesToWrite() <= socketBufferFill 97 && bytesTotal != written) 98 #endif 99 {100 // get pointer to upload data101 qint64 currentReadSize = 0;102 qint64 desiredReadSize = qMin(socketWriteMaxSize, bytesTotal - written);103 const char *readPointer = uploadByteDevice->readPointer(desiredReadSize, currentReadSize);104 105 if (currentReadSize == -1) {106 // premature eof happened107 connection->d_func()->emitReplyError(socket, reply, QNetworkReply::UnknownNetworkError);108 return false;109 break;110 } else if (readPointer == 0 || currentReadSize == 0) {111 // nothing to read currently, break the loop112 break;113 } else {114 qint64 currentWriteSize = socket->write(readPointer, currentReadSize);115 if (currentWriteSize == -1 || currentWriteSize != currentReadSize) {116 // socket broke down117 connection->d_func()->emitReplyError(socket, reply, QNetworkReply::UnknownNetworkError);118 return false;119 } else {120 written += currentWriteSize;121 uploadByteDevice->advanceReadPointer(currentWriteSize);122 123 emit reply->dataSendProgress(written, bytesTotal);124 125 if (written == bytesTotal) {126 // make sure this function is called once again127 state = QHttpNetworkConnectionChannel::WaitingState;128 sendRequest();129 break;130 }131 }132 }133 }134 break;135 }136 137 case QHttpNetworkConnectionChannel::WaitingState:138 {139 QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();140 if (uploadByteDevice) {141 QObject::disconnect(uploadByteDevice, SIGNAL(readyRead()), this, SLOT(_q_uploadDataReadyRead()));142 }143 144 // HTTP pipelining145 //connection->d_func()->fillPipeline(socket);146 //socket->flush();147 148 // ensure we try to receive a reply in all cases, even if _q_readyRead_ hat not been called149 // this is needed if the sends an reply before we have finished sending the request. In that150 // case receiveReply had been called before but ignored the server reply151 if (socket->bytesAvailable())152 QMetaObject::invokeMethod(this, "_q_receiveReply", Qt::QueuedConnection);153 break;154 }155 case QHttpNetworkConnectionChannel::ReadingState:156 // ignore _q_bytesWritten in these states157 // fall through158 default:159 break;160 }161 return true;162 }

 

进行的底层的socket调用,不详细分析

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

1 QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16 port, bool encrypt, QObject *parent, QSharedPointer
networkSession)2 : QObject(*(new QHttpNetworkConnectionPrivate(hostName, port, encrypt)), parent)3 {4 Q_D(QHttpNetworkConnection);5 d->networkSession = networkSession;6 d->init();7 }

继续跟进 init函数:

1 void QHttpNetworkConnectionPrivate::init() 2 { 3     for (int i = 0; i < channelCount; i++) { 4         channels[i].setConnection(this->q_func()); 5         channels[i].ssl = encrypt; 6 #ifndef QT_NO_BEARERMANAGEMENT 7         //push session down to channels 8         channels[i].networkSession = networkSession; 9 #endif10         channels[i].init();11     }12 }

 接下来看channels的init函数

 

1 void QHttpNetworkConnectionChannel::init() 2 { 3 #ifndef QT_NO_OPENSSL 4     if (connection->d_func()->encrypt) 5         socket = new QSslSocket; 6     else 7         socket = new QTcpSocket; 8 #else 9     socket = new QTcpSocket;10 #endif11 #ifndef QT_NO_BEARERMANAGEMENT12     //push session down to socket13     if (networkSession)14         socket->setProperty("_q_networksession", QVariant::fromValue(networkSession));15 #endif16 #ifndef QT_NO_NETWORKPROXY17     // Set by QNAM anyway, but let's be safe here18     socket->setProxy(QNetworkProxy::NoProxy);19 #endif20 21     QObject::connect(socket, SIGNAL(bytesWritten(qint64)),22                      this, SLOT(_q_bytesWritten(qint64)),23                      Qt::DirectConnection);24     QObject::connect(socket, SIGNAL(connected()),25                      this, SLOT(_q_connected()),26                      Qt::DirectConnection);27     QObject::connect(socket, SIGNAL(readyRead()),28                      this, SLOT(_q_readyRead()),29                      Qt::DirectConnection);30 31     // The disconnected() and error() signals may already come32     // while calling connectToHost().33     // In case of a cached hostname or an IP this34     // will then emit a signal to the user of QNetworkReply35     // but cannot be caught because the user did not have a chance yet36     // to connect to QNetworkReply's signals.37     qRegisterMetaType
("QAbstractSocket::SocketError");38 QObject::connect(socket, SIGNAL(disconnected()),39 this, SLOT(_q_disconnected()),40 Qt::QueuedConnection);41 QObject::connect(socket, SIGNAL(error(QAbstractSocket::SocketError)),42 this, SLOT(_q_error(QAbstractSocket::SocketError)),43 Qt::QueuedConnection);44 45 46 #ifndef QT_NO_NETWORKPROXY47 QObject::connect(socket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),48 this, SLOT(_q_proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),49 Qt::DirectConnection);50 #endif51 52 #ifndef QT_NO_OPENSSL53 QSslSocket *sslSocket = qobject_cast
(socket);54 if (sslSocket) {55 // won't be a sslSocket if encrypt is false56 QObject::connect(sslSocket, SIGNAL(encrypted()),57 this, SLOT(_q_encrypted()),58 Qt::DirectConnection);59 QObject::connect(sslSocket, SIGNAL(sslErrors(QList
)),60 this, SLOT(_q_sslErrors(QList
)),61 Qt::DirectConnection);62 QObject::connect(sslSocket, SIGNAL(encryptedBytesWritten(qint64)),63 this, SLOT(_q_encryptedBytesWritten(qint64)),64 Qt::DirectConnection);65 }66 #endif67 }

看到了我们熟悉的QTcpSocket类,该类继承于QAbstractSocket,封装了平台socket

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

1 void QNetworkAccessHttpBackend::postRequest()  2 {  3     QThread *thread = 0;  4     if (isSynchronous()) {  5         // A synchronous HTTP request uses its own thread  6         thread = new QThread();  7         QObject::connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));  8         thread->start();  9     } else if (!manager->httpThread) { 10         // We use the manager-global thread. 11         // At some point we could switch to having multiple threads if it makes sense. 12         manager->httpThread = new QThread(); 13         QObject::connect(manager->httpThread, SIGNAL(finished()), manager->httpThread, SLOT(deleteLater())); 14         manager->httpThread->start(); 15 #ifndef QT_NO_NETWORKPROXY 16         qRegisterMetaType
("QNetworkProxy"); 17 #endif 18 #ifndef QT_NO_OPENSSL 19 qRegisterMetaType
>("QList
"); 20 qRegisterMetaType
("QSslConfiguration"); 21 #endif 22 qRegisterMetaType
> >("QList
>"); 23 qRegisterMetaType
("QHttpNetworkRequest"); 24 qRegisterMetaType
("QNetworkReply::NetworkError"); 25 qRegisterMetaType
>("QSharedPointer
"); 26 27 thread = manager->httpThread; 28 } else { 29 // Asynchronous request, thread already exists 30 thread = manager->httpThread; 31 } 32 33 QUrl url = request().url(); 34 httpRequest.setUrl(url); 35 36 bool ssl = url.scheme().toLower() == QLatin1String("https"); 37 setAttribute(QNetworkRequest::ConnectionEncryptedAttribute, ssl); 38 httpRequest.setSsl(ssl); 39 40 41 #ifndef QT_NO_NETWORKPROXY 42 QNetworkProxy transparentProxy, cacheProxy; 43 44 foreach (const QNetworkProxy &p, proxyList()) { 45 // use the first proxy that works 46 // for non-encrypted connections, any transparent or HTTP proxy 47 // for encrypted, only transparent proxies 48 if (!ssl 49 && (p.capabilities() & QNetworkProxy::CachingCapability) 50 && (p.type() == QNetworkProxy::HttpProxy || 51 p.type() == QNetworkProxy::HttpCachingProxy)) { 52 cacheProxy = p; 53 transparentProxy = QNetworkProxy::NoProxy; 54 break; 55 } 56 if (p.isTransparentProxy()) { 57 transparentProxy = p; 58 cacheProxy = QNetworkProxy::NoProxy; 59 break; 60 } 61 } 62 63 // check if at least one of the proxies 64 if (transparentProxy.type() == QNetworkProxy::DefaultProxy && 65 cacheProxy.type() == QNetworkProxy::DefaultProxy) { 66 // unsuitable proxies 67 QMetaObject::invokeMethod(this, "error", isSynchronous() ? Qt::DirectConnection : Qt::QueuedConnection, 68 Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ProxyNotFoundError), 69 Q_ARG(QString, tr("No suitable proxy found"))); 70 QMetaObject::invokeMethod(this, "finished", isSynchronous() ? Qt::DirectConnection : Qt::QueuedConnection); 71 return; 72 } 73 #endif 74 75 76 bool loadedFromCache = false; 77 httpRequest.setPriority(convert(request().priority())); 78 79 switch (operation()) { 80 case QNetworkAccessManager::GetOperation: 81 httpRequest.setOperation(QHttpNetworkRequest::Get); 82 loadedFromCache = loadFromCacheIfAllowed(httpRequest); 83 break; 84 85 case QNetworkAccessManager::HeadOperation: 86 httpRequest.setOperation(QHttpNetworkRequest::Head); 87 loadedFromCache = loadFromCacheIfAllowed(httpRequest); 88 break; 89 90 case QNetworkAccessManager::PostOperation: 91 invalidateCache(); 92 httpRequest.setOperation(QHttpNetworkRequest::Post); 93 createUploadByteDevice(); 94 break; 95 96 case QNetworkAccessManager::PutOperation: 97 invalidateCache(); 98 httpRequest.setOperation(QHttpNetworkRequest::Put); 99 createUploadByteDevice();100 break;101 102 case QNetworkAccessManager::DeleteOperation:103 invalidateCache();104 httpRequest.setOperation(QHttpNetworkRequest::Delete);105 break;106 107 case QNetworkAccessManager::CustomOperation:108 invalidateCache(); // for safety reasons, we don't know what the operation does109 httpRequest.setOperation(QHttpNetworkRequest::Custom);110 createUploadByteDevice();111 httpRequest.setCustomVerb(request().attribute(112 QNetworkRequest::CustomVerbAttribute).toByteArray());113 break;114 115 default:116 break; // can't happen117 }118 119 if (loadedFromCache) {120 // commented this out since it will be called later anyway121 // by copyFinished()122 //QNetworkAccessBackend::finished();123 return; // no need to send the request! :)124 }125 126 QList
headers = request().rawHeaderList();127 if (resumeOffset != 0) {128 if (headers.contains("Range")) {129 // Need to adjust resume offset for user specified range130 131 headers.removeOne("Range");132 133 // We've already verified that requestRange starts with "bytes=", see canResume.134 QByteArray requestRange = request().rawHeader("Range").mid(6);135 136 int index = requestRange.indexOf('-');137 138 quint64 requestStartOffset = requestRange.left(index).toULongLong();139 quint64 requestEndOffset = requestRange.mid(index + 1).toULongLong();140 141 requestRange = "bytes=" + QByteArray::number(resumeOffset + requestStartOffset) +142 '-' + QByteArray::number(requestEndOffset);143 144 httpRequest.setHeaderField("Range", requestRange);145 } else {146 httpRequest.setHeaderField("Range", "bytes=" + QByteArray::number(resumeOffset) + '-');147 }148 }149 150 foreach (const QByteArray &header, headers)151 httpRequest.setHeaderField(header, request().rawHeader(header));152 153 if (request().attribute(QNetworkRequest::HttpPipeliningAllowedAttribute).toBool() == true)154 httpRequest.setPipeliningAllowed(true);155 156 if (static_cast
157 (request().attribute(QNetworkRequest::AuthenticationReuseAttribute,158 QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Manual)159 httpRequest.setWithCredentials(false);160 161 162 // Create the HTTP thread delegate163 QHttpThreadDelegate *delegate = new QHttpThreadDelegate;164 #ifndef QT_NO_BEARERMANAGEMENT165 QVariant v(property("_q_networksession"));166 if (v.isValid())167 delegate->networkSession = qvariant_cast
>(v);168 #endif169 170 // For the synchronous HTTP, this is the normal way the delegate gets deleted171 // For the asynchronous HTTP this is a safety measure, the delegate deletes itself when HTTP is finished172 connect(thread, SIGNAL(finished()), delegate, SLOT(deleteLater()));173 174 // Set the properties it needs175 delegate->httpRequest = httpRequest;176 #ifndef QT_NO_NETWORKPROXY177 delegate->cacheProxy = cacheProxy;178 delegate->transparentProxy = transparentProxy;179 #endif180 delegate->ssl = ssl;181 #ifndef QT_NO_OPENSSL182 if (ssl)183 delegate->incomingSslConfiguration = request().sslConfiguration();184 #endif185 186 // Do we use synchronous HTTP?187 delegate->synchronous = isSynchronous();188 189 // The authentication manager is used to avoid the BlockingQueuedConnection communication190 // from HTTP thread to user thread in some cases.191 delegate->authenticationManager = manager->authenticationManager;192 193 if (!isSynchronous()) {194 // Tell our zerocopy policy to the delegate195 delegate->downloadBufferMaximumSize =196 request().attribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute).toLongLong();197 198 // These atomic integers are used for signal compression199 delegate->pendingDownloadData = pendingDownloadDataEmissions;200 delegate->pendingDownloadProgress = pendingDownloadProgressEmissions;201 202 // Connect the signals of the delegate to us203 connect(delegate, SIGNAL(downloadData(QByteArray)),204 this, SLOT(replyDownloadData(QByteArray)),205 Qt::QueuedConnection);206 connect(delegate, SIGNAL(downloadFinished()),207 this, SLOT(replyFinished()),208 Qt::QueuedConnection);209 connect(delegate, SIGNAL(downloadMetaData(QList
>,int,QString,bool,QSharedPointer
,qint64)),210 this, SLOT(replyDownloadMetaData(QList
>,int,QString,bool,QSharedPointer
,qint64)),211 Qt::QueuedConnection);212 connect(delegate, SIGNAL(downloadProgress(qint64,qint64)),213 this, SLOT(replyDownloadProgressSlot(qint64,qint64)),214 Qt::QueuedConnection);215 connect(delegate, SIGNAL(error(QNetworkReply::NetworkError,QString)),216 this, SLOT(httpError(QNetworkReply::NetworkError, const QString)),217 Qt::QueuedConnection);218 #ifndef QT_NO_OPENSSL219 connect(delegate, SIGNAL(sslConfigurationChanged(QSslConfiguration)),220 this, SLOT(replySslConfigurationChanged(QSslConfiguration)),221 Qt::QueuedConnection);222 #endif223 // Those need to report back, therefire BlockingQueuedConnection224 connect(delegate, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)),225 this, SLOT(httpAuthenticationRequired(QHttpNetworkRequest,QAuthenticator*)),226 Qt::BlockingQueuedConnection);227 #ifndef QT_NO_NETWORKPROXY228 connect (delegate, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),229 this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),230 Qt::BlockingQueuedConnection);231 #endif232 #ifndef QT_NO_OPENSSL233 connect(delegate, SIGNAL(sslErrors(QList
,bool*,QList
*)),234 this, SLOT(replySslErrors(const QList
&, bool *, QList
*)),235 Qt::BlockingQueuedConnection);236 #endif237 // This signal we will use to start the request.238 connect(this, SIGNAL(startHttpRequest()), delegate, SLOT(startRequest()));239 connect(this, SIGNAL(abortHttpRequest()), delegate, SLOT(abortRequest()));240 241 // To throttle the connection.242 QObject::connect(this, SIGNAL(readBufferSizeChanged(qint64)), delegate, SLOT(readBufferSizeChanged(qint64)));243 QObject::connect(this, SIGNAL(readBufferFreed(qint64)), delegate, SLOT(readBufferFreed(qint64)));244 245 if (uploadByteDevice) {246 QNonContiguousByteDeviceThreadForwardImpl *forwardUploadDevice =247 new QNonContiguousByteDeviceThreadForwardImpl(uploadByteDevice->atEnd(), uploadByteDevice->size());248 if (uploadByteDevice->isResetDisabled())249 forwardUploadDevice->disableReset();250 forwardUploadDevice->setParent(delegate); // needed to make sure it is moved on moveToThread()251 delegate->httpRequest.setUploadByteDevice(forwardUploadDevice);252 253 // From main thread to user thread:254 QObject::connect(this, SIGNAL(haveUploadData(QByteArray, bool, qint64)),255 forwardUploadDevice, SLOT(haveDataSlot(QByteArray, bool, qint64)), Qt::QueuedConnection);256 QObject::connect(uploadByteDevice.data(), SIGNAL(readyRead()),257 forwardUploadDevice, SIGNAL(readyRead()),258 Qt::QueuedConnection);259 260 // From http thread to user thread:261 QObject::connect(forwardUploadDevice, SIGNAL(wantData(qint64)),262 this, SLOT(wantUploadDataSlot(qint64)));263 QObject::connect(forwardUploadDevice, SIGNAL(processedData(qint64)),264 this, SLOT(sentUploadDataSlot(qint64)));265 connect(forwardUploadDevice, SIGNAL(resetData(bool*)),266 this, SLOT(resetUploadDataSlot(bool*)),267 Qt::BlockingQueuedConnection); // this is the only one with BlockingQueued!268 }269 } else if (isSynchronous()) {270 connect(this, SIGNAL(startHttpRequestSynchronously()), delegate, SLOT(startRequestSynchronously()), Qt::BlockingQueuedConnection);271 272 if (uploadByteDevice) {273 // For the synchronous HTTP use case the use thread (this one here) is blocked274 // so we cannot use the asynchronous upload architecture.275 // We therefore won't use the QNonContiguousByteDeviceThreadForwardImpl but directly276 // use the uploadByteDevice provided to us by the QNetworkReplyImpl.277 // The code that is in QNetworkReplyImplPrivate::setup() makes sure it is safe to use from a thread278 // since it only wraps a QRingBuffer279 delegate->httpRequest.setUploadByteDevice(uploadByteDevice.data());280 }281 }282 283 284 // Move the delegate to the http thread285 delegate->moveToThread(thread);286 // This call automatically moves the uploadDevice too for the asynchronous case.287 288 // Send an signal to the delegate so it starts working in the other thread289 if (isSynchronous()) {290 emit startHttpRequestSynchronously(); // This one is BlockingQueuedConnection, so it will return when all work is done291 292 if (delegate->incomingErrorCode != QNetworkReply::NoError) {293 replyDownloadMetaData294 (delegate->incomingHeaders,295 delegate->incomingStatusCode,296 delegate->incomingReasonPhrase,297 delegate->isPipeliningUsed,298 QSharedPointer
(),299 delegate->incomingContentLength);300 replyDownloadData(delegate->synchronousDownloadData);301 httpError(delegate->incomingErrorCode, delegate->incomingErrorDetail);302 } else {303 replyDownloadMetaData304 (delegate->incomingHeaders,305 delegate->incomingStatusCode,306 delegate->incomingReasonPhrase,307 delegate->isPipeliningUsed,308 QSharedPointer
(),309 delegate->incomingContentLength);310 replyDownloadData(delegate->synchronousDownloadData);311 }312 313 // End the thread. It will delete itself from the finished() signal314 thread->quit();315 thread->wait(5000);316 317 finished();318 } else {319 emit startHttpRequest(); // Signal to the HTTP thread and go back to user.320 }321 }

完了下面这些动作:

1、看Cache中是否保存有过去浏览的内容,如果有还要看是否超出生存时间(Expiration
Time);
2、设定Url、Header和数据内容(需要提交的数据);
3、调用QNetworkAccessHttpBackendCache::sendRequest()发送请求内容;
4、把QHttpNetworkReply的信号与QNetworkAccessHttpBackend的槽连接起来,完成后续处理。

 

分析QNetworkAccessManager的时候,有一段设定HTTP的请求包的Header,当时没进行深入的分析。

1 void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair) 2 { 3     QHttpNetworkRequest &request = messagePair.first; 4     QHttpNetworkReply *reply = messagePair.second; 5  6     // add missing fields for the request 7     QByteArray value; 8     // check if Content-Length is provided 9     QIODevice *data = request.data();10     if (data && request.contentLength() == -1) {11         if (!data->isSequential())12             request.setContentLength(data->size());13         else14             bufferData(messagePair); // ### or do chunked upload15     }16     // set the Connection/Proxy-Connection: Keep-Alive headers17 #ifndef QT_NO_NETWORKPROXY18     if (networkProxy.type() == QNetworkProxy::HttpCachingProxy)  {19         value = request.headerField("proxy-connection");20         if (value.isEmpty())21             request.setHeaderField("Proxy-Connection", "Keep-Alive");22     } else {23 #endif24         value = request.headerField("connection");25         if (value.isEmpty())26             request.setHeaderField("Connection", "Keep-Alive");27 #ifndef QT_NO_NETWORKPROXY28     }29 #endif30 31     // If the request had a accept-encoding set, we better not mess32     // with it. If it was not set, we announce that we understand gzip33     // and remember this fact in request.d->autoDecompress so that34     // we can later decompress the HTTP reply if it has such an35     // encoding.36     value = request.headerField("accept-encoding");37     if (value.isEmpty()) {38 #ifndef QT_NO_COMPRESS39         request.setHeaderField("Accept-Encoding", "gzip");40         request.d->autoDecompress = true;41 #else42         // if zlib is not available set this to false always43         request.d->autoDecompress = false;44 #endif45     }46     // set the User Agent47     value = request.headerField("user-agent");48     if (value.isEmpty())49         request.setHeaderField("User-Agent", "Mozilla/5.0");50     // set the host51     value = request.headerField("host");52     if (value.isEmpty()) {53         QByteArray host = QUrl::toAce(hostName);54 55         int port = request.url().port();56         if (port != -1) {57             host += ':';58             host += QByteArray::number(port);59         }60 61         request.setHeaderField("Host", host);62     }63 64     reply->d_func()->requestIsPrepared = true;65 }

 

如果想模拟IE浏览器,或者想修改成任何你希望的信息,就是在这里修改。

在设定了这些请求信息之后,发送的请求信息包是什么样子的呢?我把工具拦截的信息包具体情况贴出来

转载地址:http://rjhql.baihongyu.com/

你可能感兴趣的文章
委托事件模型
查看>>
Win8 内置游戏应用更新 中文翻译错误得以修正
查看>>
When the nofile is set to unlimited in /etc/security/limits.conf file the user cannot login
查看>>
普元:商业功能虚拟化的企业软件将走向下一个10年
查看>>
linux下如何完全删除用户账号
查看>>
2.6.29内核中对vmscan的一个改进
查看>>
修改3389端口的方法
查看>>
查看mysql数据库使用大小和mysql修改时间
查看>>
exchange 2013 升级CU15,提示“上次安装完成后没有重启”的提示
查看>>
sed实例(持续更新)
查看>>
12月13日笔记 远程连接Centos7系统
查看>>
Docker 数据卷
查看>>
python反射
查看>>
RHEL7搭建LAMP环境并安装Discuz论坛
查看>>
python3 字符串操作
查看>>
网络工程师学习笔记之WLAN(IEEE802.11)
查看>>
阿里云初步学习有感,建设网站!
查看>>
查询优化器内核剖析第五篇:进一步的了解执行计划
查看>>
半夜一次数据导入导出主从同步及解决方案
查看>>
学习笔记:rsync命令实战
查看>>