QT分析之網絡編程


原文地址: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_NETWORKPROXY
29     // Get the proxy information
30     d->resolveProxy(hostName, port);
31     if (d->proxyInUse.type() == QNetworkProxy::DefaultProxy) {
32         // failed to setup the proxy
33         d->socketError = QAbstractSocket::UnsupportedSocketOperationError;
34         setErrorString(QAbstractSocket::tr("Operation on socket is not supported"));
35         emit error(d->socketError);
36         return;
37     }
38 #endif
39 
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<QHostAddress>() << temp);
50         d->_q_startConnecting(info);
51 #ifndef QT_NO_NETWORKPROXY
52     } else if (d->proxyInUse.capabilities() & QNetworkProxy::HostNameLookupCapability) {
53         // the proxy supports connection by name, so use it
54         d->startConnectingByName(hostName);
55         return;
56 #endif
57     } 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::UnknownSocketError
10                     ) && 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 candidate
27         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 to
32             // connect. So we just pretend we didn't see this address.
33             continue;
34         }
35 #endif
36 
37         if (!initSocketLayer(host.protocol())) {
38             // hope that the next address is better
39             continue;
40         }
41 
42         // Tries to connect to the address. If it succeeds immediately
43         // (localhost address on BSD or any UDP connect), emit
44         // 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 yet
52         cachedSocketDescriptor = socketEngine->socketDescriptor();
53 
54         // Check that we're in delayed connection state. If not, try
55         // the next address
56         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 call
72         // _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_NETWORKPROXY
32     // proxy type must have been resolved by now
33     if (proxy.type() == QNetworkProxy::DefaultProxy)
34         return 0;
35 #endif
36 
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 #endif
14 
15     // Create the socket
16     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::UdpSocket
30         && !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 data
38     if (socketType == QAbstractSocket::TcpSocket
39         && !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, found
44     // 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 #endif
13     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 EOF
10         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 future
22         // if we're not connected, return -1 indicating EOF
23         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 close
10     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 #endif
12     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::read
26             ret = 0;
27             break;
28         default:
29             break;
30         }
31     } else {
32         if (WSAGetLastError() == WSAEWOULDBLOCK)
33             ret = -2;
34         else
35             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     else
27         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 WSAENOBUFS
14     for (;;) {
15         qint64 bytesToSend = qMin<qint64>(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             else
30                 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<enum QEventLoop::ProcessEventsFlag> flags={...})  行679 + 0x10 字節    C++
18      QtGuid4.dll!QGuiEventDispatcherWin32::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags={...})  行1182 + 0x15 字節    C++
19      QtCored4.dll!QEventLoop::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags={...})  行150    C++
20      QtCored4.dll!QEventLoop::exec(QFlags<enum QEventLoop::ProcessEventsFlag> 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 字節    C
25      fortuneclient.exe!WinMainCRTStartup()  行399    C
26      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<QNetworkRequest::CacheLoadControl>(
 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<QNetworkRequest::LoadControl>
 71         (request.attribute(QNetworkRequest::CookieLoadControlAttribute,
 72                            QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Automatic) {
 73         if (d->cookieJar) {
 74             QList<QNetworkCookie> 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_OPENSSL
105     reply->setSslConfiguration(request.sslConfiguration());
106 #endif
107 
108     // fourth step: setup the reply
109     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<QSslError>)), SLOT(_q_replySslErrors(QList<QSslError>)));
10 #endif
11 #ifndef QT_NO_BEARERMANAGEMENT
12     activeReplyCount++;
13 #endif
14 
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 Ajax
13     // in QtWebKit.
14     QVariant synchronousHttpAttribute = req.attribute(
15             static_cast<QNetworkRequest::Attribute>(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<QRingBuffer>(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 or
36             // fixed size non-sequential
37             // just start the operation
38             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 buffering
46                 // if not, we will buffer anyway
47                 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, without
61         // invoking methods in a QueuedConnection
62 #ifndef QT_NO_HTTP
63         if (qobject_cast<QNetworkAccessHttpBackend *>(backend) || (backend && backend->isSynchronous())) {
64             _q_startOperation();
65         } else {
66             QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
67         }
68 #else
69         if (backend && backend->isSynchronous())
70             _q_startOperation();
71         else
72             QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
73 #endif // QT_NO_HTTP
74         }
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 (see
12     // 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_BEARERMANAGEMENT
22         // backend failed to start because the session state is not Connected.
23         // QNetworkAccessManager will call _q_startOperation again for us when the session
24         // state changes.
25         state = WaitingForSession;
26 
27         QSharedPointer<QNetworkSession> 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 #else
45         qWarning("Backend start failed");
46         state = Working;
47         error(QNetworkReplyImpl::UnknownNetworkError,
48               QCoreApplication::translate("QNetworkReply", "backend start error."));
49         finished();
50 #endif
51         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<QRingBuffer>(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 buffer
17     forever {
18         bytesToBuffer = outgoingData->bytesAvailable();
19         // unknown? just try 2 kB, this also ensures we always try to read the EOF
20         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 again
34             outgoingDataBuffer->chop(bytesToBuffer);
35 
36             break;
37         } else {
38             // don't break, try to read() again
39             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             else
19                 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 request
13             }
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<TYPE > 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 QNetworkAccessBackendFactory
2 class QNetworkAccessDebugPipeBackendFactory: public QNetworkAccessBackendFactory
3 class QNetworkAccessFileBackendFactory: public QNetworkAccessBackendFactory
4 class QNetworkAccessFtpBackendFactory: public QNetworkAccessBackendFactory
5 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 request
17         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<QNetworkSession> 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 backend
12             setProperty("_q_networksession", QVariant::fromValue(networkSession));
13         } else {
14             // Session not ready, but can skip for loopback connections
15 
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 opened
25                 return false;
26             }
27         }
28     }
29 #endif
30 
31 #ifndef QT_NO_NETWORKPROXY
32 #ifndef QT_NO_BEARERMANAGEMENT
33     // 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 use
40         QVariant v = session->sessionProperty(QLatin1String("ActiveConfiguration"));
41         if (v.isValid())
42             config = configManager.configurationFromIdentifier(qvariant_cast<QString>(v));
43         // Fallback to using the configuration if no active configuration
44         if (!config.isValid())
45             config = session->configuration();
46         // or unspecified configuration if that is no good either
47         if (!config.isValid())
48             config = QNetworkConfiguration();
49     }
50     reply->proxyList = manager->queryProxy(QNetworkProxyQuery(config, url()));
51 #else // QT_NO_BEARERMANAGEMENT
52     // Without bearer management, the proxy depends only on the url
53     reply->proxyList = manager->queryProxy(QNetworkProxyQuery(url()));
54 #endif
55 #endif
56 
57     // now start the request
58     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>("QNetworkProxy");
 17 #endif
 18 #ifndef QT_NO_OPENSSL
 19         qRegisterMetaType<QList<QSslError> >("QList<QSslError>");
 20         qRegisterMetaType<QSslConfiguration>("QSslConfiguration");
 21 #endif
 22         qRegisterMetaType<QList<QPair<QByteArray,QByteArray> > >("QList<QPair<QByteArray,QByteArray> >");
 23         qRegisterMetaType<QHttpNetworkRequest>("QHttpNetworkRequest");
 24         qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError");
 25         qRegisterMetaType<QSharedPointer<char> >("QSharedPointer<char>");
 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 does
109         httpRequest.setOperation(QHttpNetworkRequest::Custom);
110         createUploadByteDevice();
111         httpRequest.setCustomVerb(request().attribute(
112                 QNetworkRequest::CustomVerbAttribute).toByteArray());
113         break;
114 
115     default:
116         break;                  // can't happen
117     }
118 
119     if (loadedFromCache) {
120         // commented this out since it will be called later anyway
121         // by copyFinished()
122         //QNetworkAccessBackend::finished();
123         return;    // no need to send the request! :)
124     }
125 
126     QList<QByteArray> headers = request().rawHeaderList();
127     if (resumeOffset != 0) {
128         if (headers.contains("Range")) {
129             // Need to adjust resume offset for user specified range
130 
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<QNetworkRequest::LoadControl>
157         (request().attribute(QNetworkRequest::AuthenticationReuseAttribute,
158                              QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Manual)
159         httpRequest.setWithCredentials(false);
160 
161 
162     // Create the HTTP thread delegate
163     QHttpThreadDelegate *delegate = new QHttpThreadDelegate;
164 #ifndef QT_NO_BEARERMANAGEMENT
165     QVariant v(property("_q_networksession"));
166     if (v.isValid())
167         delegate->networkSession = qvariant_cast<QSharedPointer<QNetworkSession> >(v);
168 #endif
169 
170     // For the synchronous HTTP, this is the normal way the delegate gets deleted
171     // For the asynchronous HTTP this is a safety measure, the delegate deletes itself when HTTP is finished
172     connect(thread, SIGNAL(finished()), delegate, SLOT(deleteLater()));
173 
174     // Set the properties it needs
175     delegate->httpRequest = httpRequest;
176 #ifndef QT_NO_NETWORKPROXY
177     delegate->cacheProxy = cacheProxy;
178     delegate->transparentProxy = transparentProxy;
179 #endif
180     delegate->ssl = ssl;
181 #ifndef QT_NO_OPENSSL
182     if (ssl)
183         delegate->incomingSslConfiguration = request().sslConfiguration();
184 #endif
185 
186     // Do we use synchronous HTTP?
187     delegate->synchronous = isSynchronous();
188 
189     // The authentication manager is used to avoid the BlockingQueuedConnection communication
190     // 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 delegate
195         delegate->downloadBufferMaximumSize =
196                 request().attribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute).toLongLong();
197 
198         // These atomic integers are used for signal compression
199         delegate->pendingDownloadData = pendingDownloadDataEmissions;
200         delegate->pendingDownloadProgress = pendingDownloadProgressEmissions;
201 
202         // Connect the signals of the delegate to us
203         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<QPair<QByteArray,QByteArray> >,int,QString,bool,QSharedPointer<char>,qint64)),
210                 this, SLOT(replyDownloadMetaData(QList<QPair<QByteArray,QByteArray> >,int,QString,bool,QSharedPointer<char>,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_OPENSSL
219         connect(delegate, SIGNAL(sslConfigurationChanged(QSslConfiguration)),
220                 this, SLOT(replySslConfigurationChanged(QSslConfiguration)),
221                 Qt::QueuedConnection);
222 #endif
223         // Those need to report back, therefire BlockingQueuedConnection
224         connect(delegate, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
225                 this, SLOT(httpAuthenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
226                 Qt::BlockingQueuedConnection);
227 #ifndef QT_NO_NETWORKPROXY
228         connect (delegate, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
229                  this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
230                  Qt::BlockingQueuedConnection);
231 #endif
232 #ifndef QT_NO_OPENSSL
233         connect(delegate, SIGNAL(sslErrors(QList<QSslError>,bool*,QList<QSslError>*)),
234                 this, SLOT(replySslErrors(const QList<QSslError> &, bool *, QList<QSslError> *)),
235                 Qt::BlockingQueuedConnection);
236 #endif
237         // 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 blocked
274             // so we cannot use the asynchronous upload architecture.
275             // We therefore won't use the QNonContiguousByteDeviceThreadForwardImpl but directly
276             // 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 thread
278             // since it only wraps a QRingBuffer
279             delegate->httpRequest.setUploadByteDevice(uploadByteDevice.data());
280         }
281     }
282 
283 
284     // Move the delegate to the http thread
285     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 thread
289     if (isSynchronous()) {
290         emit startHttpRequestSynchronously(); // This one is BlockingQueuedConnection, so it will return when all work is done
291 
292         if (delegate->incomingErrorCode != QNetworkReply::NoError) {
293             replyDownloadMetaData
294                     (delegate->incomingHeaders,
295                      delegate->incomingStatusCode,
296                      delegate->incomingReasonPhrase,
297                      delegate->isPipeliningUsed,
298                      QSharedPointer<char>(),
299                      delegate->incomingContentLength);
300             replyDownloadData(delegate->synchronousDownloadData);
301             httpError(delegate->incomingErrorCode, delegate->incomingErrorDetail);
302         } else {
303             replyDownloadMetaData
304                     (delegate->incomingHeaders,
305                      delegate->incomingStatusCode,
306                      delegate->incomingReasonPhrase,
307                      delegate->isPipeliningUsed,
308                      QSharedPointer<char>(),
309                      delegate->incomingContentLength);
310             replyDownloadData(delegate->synchronousDownloadData);
311         }
312 
313         // End the thread. It will delete itself from the finished() signal
314         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 host
13     QUrl urlCopy = httpRequest.url();
14     urlCopy.setPort(urlCopy.port(ssl ? 443 : 80));
15 
16 #ifndef QT_NO_NETWORKPROXY
17     if (transparentProxy.type() != QNetworkProxy::NoProxy)
18         cacheKey = makeCacheKey(urlCopy, &transparentProxy);
19     else if (cacheProxy.type() != QNetworkProxy::NoProxy)
20         cacheKey = makeCacheKey(urlCopy, &cacheProxy);
21     else
22 #endif
23         cacheKey = makeCacheKey(urlCopy, 0);
24 
25 
26     // the http object is actually a QHttpNetworkConnection
27     httpConnection = static_cast<QNetworkAccessCachedHttpConnection *>(connections.localData()->requestEntryNow(cacheKey));
28     if (httpConnection == 0) {
29         // no entry in cache; create an object
30         // the http object is actually a QHttpNetworkConnection
31 #ifdef QT_NO_BEARERMANAGEMENT
32         httpConnection = new QNetworkAccessCachedHttpConnection(urlCopy.host(), urlCopy.port(), ssl);
33 #else
34         httpConnection = new QNetworkAccessCachedHttpConnection(urlCopy.host(), urlCopy.port(), ssl, networkSession);
35 #endif
36 #ifndef QT_NO_OPENSSL
37         // Set the QSslConfiguration from this QNetworkRequest.
38         if (ssl && incomingSslConfiguration != QSslConfiguration::defaultConfiguration()) {
39             httpConnection->setSslConfiguration(incomingSslConfiguration);
40         }
41 #endif
42 
43 #ifndef QT_NO_NETWORKPROXY
44         httpConnection->setTransparentProxy(transparentProxy);
45         httpConnection->setCacheProxy(cacheProxy);
46 #endif
47 
48         // cache the QHttpNetworkConnection corresponding to this cache key
49         connections.localData()->addEntry(cacheKey, httpConnection);
50     }
51 
52 
53     // Send the request to the connection
54     httpReply = httpConnection->sendRequest(httpRequest);
55     httpReply->setParent(this);
56 
57     // Connect the reply signals that we need to handle and then forward
58     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 used
76         connect(httpReply,SIGNAL(readyRead()), this, SLOT(readyReadSlot()));
77         connect(httpReply,SIGNAL(dataReadProgress(int, int)), this, SLOT(dataReadProgressSlot(int,int)));
78 #ifndef QT_NO_OPENSSL
79         connect(httpReply,SIGNAL(sslErrors(const QList<QSslError>)), this, SLOT(sslErrorsSlot(QList<QSslError>)));
80 #endif
81 
82         // In the asynchronous HTTP case we can just forward those signals
83         // Connect the reply signals that we can directly forward
84         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 later
10     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 QueuedConnection
23     // It is the only place _q_startNextRequest is called directly without going
24     // through the event loop using a QueuedConnection.
25     // This is dangerous because of recursion that might occur when emitting
26     // signals as DirectConnection from this code path. Therefore all signal
27     // emissions that can come out from this code path need to
28     // 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 terminated
14             if (!channels[i].resetUploadData())
15                 continue;
16             channels[i].sendRequest();
17         }
18     }
19 
20     // dequeue new ones
21 
22     // return fast if there is nothing to do
23     if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty())
24         return;
25     // try to get a free AND connected socket
26     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 sockets
34     // ### FIXME we should move this to the beginning of the function
35     // as soon as QtWebkit is properly using the pipelining
36     // (e.g. not for XMLHttpRequest or the first page load)
37     // ### FIXME we should also divide the requests more even
38     // on the connected sockets
39     //tryToFillPipeline(socket);
40     // return fast if there is nothing to pipeline
41     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 again
19         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<QSslSocket*>(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 data
101             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 happened
107                 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 loop
112                 break;
113             } else {
114                 qint64 currentWriteSize = socket->write(readPointer, currentReadSize);
115                 if (currentWriteSize == -1 || currentWriteSize != currentReadSize) {
116                     // socket broke down
117                     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 again
127                         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 pipelining
145         //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 called
149         // this is needed if the sends an reply before we have finished sending the request. In that
150         // case receiveReply had been called before but ignored the server reply
151         if (socket->bytesAvailable())
152             QMetaObject::invokeMethod(this, "_q_receiveReply", Qt::QueuedConnection);
153         break;
154     }
155     case QHttpNetworkConnectionChannel::ReadingState:
156         // ignore _q_bytesWritten in these states
157         // fall through
158     default:
159         break;
160     }
161     return true;
162 }

 

進行的底層的socket調用,不詳細分析

QHttpNetworkConnection的構造中,有些我們感興趣的東西:

1 QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16 port, bool encrypt, QObject *parent, QSharedPointer<QNetworkSession> 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 #endif
10         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 #endif
11 #ifndef QT_NO_BEARERMANAGEMENT
12     //push session down to socket
13     if (networkSession)
14         socket->setProperty("_q_networksession", QVariant::fromValue(networkSession));
15 #endif
16 #ifndef QT_NO_NETWORKPROXY
17     // Set by QNAM anyway, but let's be safe here
18     socket->setProxy(QNetworkProxy::NoProxy);
19 #endif
20 
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 come
32     // while calling connectToHost().
33     // In case of a cached hostname or an IP this
34     // will then emit a signal to the user of QNetworkReply
35     // but cannot be caught because the user did not have a chance yet
36     // to connect to QNetworkReply's signals.
37     qRegisterMetaType<QAbstractSocket::SocketError>("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_NETWORKPROXY
47     QObject::connect(socket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
48                      this, SLOT(_q_proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
49                      Qt::DirectConnection);
50 #endif
51 
52 #ifndef QT_NO_OPENSSL
53     QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
54     if (sslSocket) {
55         // won't be a sslSocket if encrypt is false
56         QObject::connect(sslSocket, SIGNAL(encrypted()),
57                          this, SLOT(_q_encrypted()),
58                          Qt::DirectConnection);
59         QObject::connect(sslSocket, SIGNAL(sslErrors(QList<QSslError>)),
60                          this, SLOT(_q_sslErrors(QList<QSslError>)),
61                          Qt::DirectConnection);
62         QObject::connect(sslSocket, SIGNAL(encryptedBytesWritten(qint64)),
63                          this, SLOT(_q_encryptedBytesWritten(qint64)),
64                          Qt::DirectConnection);
65     }
66 #endif
67 }

看到了我們熟悉的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>("QNetworkProxy");
 17 #endif
 18 #ifndef QT_NO_OPENSSL
 19         qRegisterMetaType<QList<QSslError> >("QList<QSslError>");
 20         qRegisterMetaType<QSslConfiguration>("QSslConfiguration");
 21 #endif
 22         qRegisterMetaType<QList<QPair<QByteArray,QByteArray> > >("QList<QPair<QByteArray,QByteArray> >");
 23         qRegisterMetaType<QHttpNetworkRequest>("QHttpNetworkRequest");
 24         qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError");
 25         qRegisterMetaType<QSharedPointer<char> >("QSharedPointer<char>");
 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 does
109         httpRequest.setOperation(QHttpNetworkRequest::Custom);
110         createUploadByteDevice();
111         httpRequest.setCustomVerb(request().attribute(
112                 QNetworkRequest::CustomVerbAttribute).toByteArray());
113         break;
114 
115     default:
116         break;                  // can't happen
117     }
118 
119     if (loadedFromCache) {
120         // commented this out since it will be called later anyway
121         // by copyFinished()
122         //QNetworkAccessBackend::finished();
123         return;    // no need to send the request! :)
124     }
125 
126     QList<QByteArray> headers = request().rawHeaderList();
127     if (resumeOffset != 0) {
128         if (headers.contains("Range")) {
129             // Need to adjust resume offset for user specified range
130 
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<QNetworkRequest::LoadControl>
157         (request().attribute(QNetworkRequest::AuthenticationReuseAttribute,
158                              QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Manual)
159         httpRequest.setWithCredentials(false);
160 
161 
162     // Create the HTTP thread delegate
163     QHttpThreadDelegate *delegate = new QHttpThreadDelegate;
164 #ifndef QT_NO_BEARERMANAGEMENT
165     QVariant v(property("_q_networksession"));
166     if (v.isValid())
167         delegate->networkSession = qvariant_cast<QSharedPointer<QNetworkSession> >(v);
168 #endif
169 
170     // For the synchronous HTTP, this is the normal way the delegate gets deleted
171     // For the asynchronous HTTP this is a safety measure, the delegate deletes itself when HTTP is finished
172     connect(thread, SIGNAL(finished()), delegate, SLOT(deleteLater()));
173 
174     // Set the properties it needs
175     delegate->httpRequest = httpRequest;
176 #ifndef QT_NO_NETWORKPROXY
177     delegate->cacheProxy = cacheProxy;
178     delegate->transparentProxy = transparentProxy;
179 #endif
180     delegate->ssl = ssl;
181 #ifndef QT_NO_OPENSSL
182     if (ssl)
183         delegate->incomingSslConfiguration = request().sslConfiguration();
184 #endif
185 
186     // Do we use synchronous HTTP?
187     delegate->synchronous = isSynchronous();
188 
189     // The authentication manager is used to avoid the BlockingQueuedConnection communication
190     // 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 delegate
195         delegate->downloadBufferMaximumSize =
196                 request().attribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute).toLongLong();
197 
198         // These atomic integers are used for signal compression
199         delegate->pendingDownloadData = pendingDownloadDataEmissions;
200         delegate->pendingDownloadProgress = pendingDownloadProgressEmissions;
201 
202         // Connect the signals of the delegate to us
203         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<QPair<QByteArray,QByteArray> >,int,QString,bool,QSharedPointer<char>,qint64)),
210                 this, SLOT(replyDownloadMetaData(QList<QPair<QByteArray,QByteArray> >,int,QString,bool,QSharedPointer<char>,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_OPENSSL
219         connect(delegate, SIGNAL(sslConfigurationChanged(QSslConfiguration)),
220                 this, SLOT(replySslConfigurationChanged(QSslConfiguration)),
221                 Qt::QueuedConnection);
222 #endif
223         // Those need to report back, therefire BlockingQueuedConnection
224         connect(delegate, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
225                 this, SLOT(httpAuthenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
226                 Qt::BlockingQueuedConnection);
227 #ifndef QT_NO_NETWORKPROXY
228         connect (delegate, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
229                  this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
230                  Qt::BlockingQueuedConnection);
231 #endif
232 #ifndef QT_NO_OPENSSL
233         connect(delegate, SIGNAL(sslErrors(QList<QSslError>,bool*,QList<QSslError>*)),
234                 this, SLOT(replySslErrors(const QList<QSslError> &, bool *, QList<QSslError> *)),
235                 Qt::BlockingQueuedConnection);
236 #endif
237         // 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 blocked
274             // so we cannot use the asynchronous upload architecture.
275             // We therefore won't use the QNonContiguousByteDeviceThreadForwardImpl but directly
276             // 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 thread
278             // since it only wraps a QRingBuffer
279             delegate->httpRequest.setUploadByteDevice(uploadByteDevice.data());
280         }
281     }
282 
283 
284     // Move the delegate to the http thread
285     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 thread
289     if (isSynchronous()) {
290         emit startHttpRequestSynchronously(); // This one is BlockingQueuedConnection, so it will return when all work is done
291 
292         if (delegate->incomingErrorCode != QNetworkReply::NoError) {
293             replyDownloadMetaData
294                     (delegate->incomingHeaders,
295                      delegate->incomingStatusCode,
296                      delegate->incomingReasonPhrase,
297                      delegate->isPipeliningUsed,
298                      QSharedPointer<char>(),
299                      delegate->incomingContentLength);
300             replyDownloadData(delegate->synchronousDownloadData);
301             httpError(delegate->incomingErrorCode, delegate->incomingErrorDetail);
302         } else {
303             replyDownloadMetaData
304                     (delegate->incomingHeaders,
305                      delegate->incomingStatusCode,
306                      delegate->incomingReasonPhrase,
307                      delegate->isPipeliningUsed,
308                      QSharedPointer<char>(),
309                      delegate->incomingContentLength);
310             replyDownloadData(delegate->synchronousDownloadData);
311         }
312 
313         // End the thread. It will delete itself from the finished() signal
314         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         else
14             bufferData(messagePair); // ### or do chunked upload
15     }
16     // set the Connection/Proxy-Connection: Keep-Alive headers
17 #ifndef QT_NO_NETWORKPROXY
18     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 #endif
24         value = request.headerField("connection");
25         if (value.isEmpty())
26             request.setHeaderField("Connection", "Keep-Alive");
27 #ifndef QT_NO_NETWORKPROXY
28     }
29 #endif
30 
31     // If the request had a accept-encoding set, we better not mess
32     // with it. If it was not set, we announce that we understand gzip
33     // and remember this fact in request.d->autoDecompress so that
34     // we can later decompress the HTTP reply if it has such an
35     // encoding.
36     value = request.headerField("accept-encoding");
37     if (value.isEmpty()) {
38 #ifndef QT_NO_COMPRESS
39         request.setHeaderField("Accept-Encoding", "gzip");
40         request.d->autoDecompress = true;
41 #else
42         // if zlib is not available set this to false always
43         request.d->autoDecompress = false;
44 #endif
45     }
46     // set the User Agent
47     value = request.headerField("user-agent");
48     if (value.isEmpty())
49         request.setHeaderField("User-Agent", "Mozilla/5.0");
50     // set the host
51     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瀏覽器,或者想修改成任何你希望的信息,就是在這里修改。
在設定了這些請求信息之后,發送的請求信息包是什么樣子的呢?我把工具攔截的信息包具體情況貼出來


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM