QT分析之网络编程

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

 

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

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

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

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

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

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

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

1 QNativeSocketEngine : public QAbstractSocketEngine : public QObject

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

 

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

 

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

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

 

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

 

调用的是QAbstractSocket::connectToHostImplementation()。

 

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

 

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

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

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

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

 

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

 

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

 

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

2、connect到远程目标

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

 

连接相对简单。

 

3、读取信息

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

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

 

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

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

 

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

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

 

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

 

4、发送数据

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

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

查看QNativeSocketEngine::write():

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

 

至此分析完毕。

 

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

1 QEventDispatcherWin32Private::doWsaAsyncSelect()

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

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

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

 

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

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

 

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

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

 

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

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

 

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

 

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

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

 

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

QT分析之网络编程_第1张图片

你可能感兴趣的:(QT分析之网络编程)