5、QT分析之网络编程

时间:2022-11-13 20:11:55

原文地址: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()。

5、QT分析之网络编程
1 void Client::requestNewFortune()
2 {
3 getFortuneButton->setEnabled(false);
4 blockSize = 0;
5 tcpSocket->abort();
6 tcpSocket->connectToHost(hostLineEdit->text(), portLineEdit->text().toInt());
7 }
5、QT分析之网络编程

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

5、QT分析之网络编程
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 }
5、QT分析之网络编程

调用的是QAbstractSocket::connectToHostImplementation()。

5、QT分析之网络编程
 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 }
5、QT分析之网络编程

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

5、QT分析之网络编程
 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 }
5、QT分析之网络编程

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

5、QT分析之网络编程
 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 }
5、QT分析之网络编程

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

5、QT分析之网络编程
 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 }
5、QT分析之网络编程

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

2、connect到远程目标

5、QT分析之网络编程
 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 }
5、QT分析之网络编程

连接相对简单。

3、读取信息

在QAbstractSocket中,有两个成员是收发数据用的:readData()、writeData()
readData()有两种读取方式:有缓冲和无缓冲方式。基本原理是一致的,简单其见只分析无缓冲直接读取方式。

5、QT分析之网络编程
 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 }
5、QT分析之网络编程

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

5、QT分析之网络编程
 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 }
5、QT分析之网络编程

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

5、QT分析之网络编程
 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 }
5、QT分析之网络编程

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

4、发送数据

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

5、QT分析之网络编程
 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 }
5、QT分析之网络编程

查看QNativeSocketEngine::write():

5、QT分析之网络编程
 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 }
5、QT分析之网络编程

至此分析完毕。

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

1 QEventDispatcherWin32Private::doWsaAsyncSelect()

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

5、QT分析之网络编程
 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()
5、QT分析之网络编程

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

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

5、QT分析之网络编程
 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 }
5、QT分析之网络编程

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

5、QT分析之网络编程
 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 }
5、QT分析之网络编程

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

5、QT分析之网络编程
 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 }
5、QT分析之网络编程

在这里跟前面分析的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方法;第二个参数是目标网址。

5、QT分析之网络编程
  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 }
5、QT分析之网络编程

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

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

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

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

4、设定Reply

5、获取一个backend实体

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

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

5、QT分析之网络编程
 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 }
5、QT分析之网络编程

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

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

5、QT分析之网络编程
 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 }
5、QT分析之网络编程

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

5、QT分析之网络编程
 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 }
5、QT分析之网络编程
5、QT分析之网络编程
 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 }
5、QT分析之网络编程

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

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

5、QT分析之网络编程
 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 }
5、QT分析之网络编程

该函数主要用于处理各种socket相关事件
因此我们先看QNetworkAccessManagerPrivate::findBackend()的代码实现

5、QT分析之网络编程
 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 }
5、QT分析之网络编程

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

1 Q_GLOBAL_STATIC(QNetworkAccessBackendFactoryData, factoryData)

宏定义如下:

5、QT分析之网络编程
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 }
5、QT分析之网络编程

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

5、QT分析之网络编程
 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 }
5、QT分析之网络编程

里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()

5、QT分析之网络编程
 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 }
5、QT分析之网络编程

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

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

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

5、QT分析之网络编程
 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 }
5、QT分析之网络编程

start函数很简单,主要是打开QNetworkAccessBackend
话说昨日走到QNetworkReplyImplPrivate::_q_startOperation(),勾引出QNetworkAccessHttpBackend::open(),今日接着欣赏QT之美丽

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

open函数仅仅是调用postRequest()

5、QT分析之网络编程
  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 }
5、QT分析之网络编程

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

5、QT分析之网络编程
 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 }
5、QT分析之网络编程

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

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

sendRequest()调用queueRequest()函数

5、QT分析之网络编程
 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 }
5、QT分析之网络编程

在这里整个消息处理(或者是初始化动作)完成之后,按消息序列调用_q_startNextRequest
循环多通道处理请求,类似于connect流程

5、QT分析之网络编程
 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 }
5、QT分析之网络编程

接着调用看代码

5、QT分析之网络编程
 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 }
5、QT分析之网络编程

看看prepareReuest

5、QT分析之网络编程
  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 }
5、QT分析之网络编程

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

5、QT分析之网络编程
  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 }
5、QT分析之网络编程

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

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

5、QT分析之网络编程
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 }
5、QT分析之网络编程

继续跟进 init函数:

5、QT分析之网络编程
 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 }
5、QT分析之网络编程

接下来看channels的init函数

5、QT分析之网络编程
 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 }
5、QT分析之网络编程

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

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

5、QT分析之网络编程
  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 }
5、QT分析之网络编程

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

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

5、QT分析之网络编程
 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 }
5、QT分析之网络编程

如果想模拟IE浏览器,或者想修改成任何你希望的信息,就是在这里修改。
在设定了这些请求信息之后,发送的请求信息包是什么样子的呢?我把工具拦截的信息包具体情况贴出来

5、QT分析之网络编程

有一种落差是,你配不上自己的野心,也辜负了所受的苦难。
 
转自:http://www.cnblogs.com/lfsblack/p/5279813.html