您的位置:首页 > 编程语言 > Qt开发

QT分析之网络编程

2016-03-15 15:47 639 查看
原文地址:http://blog.163.com/net_worm/blog/static/127702419201002842553382/

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

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

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


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

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类的继承:

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

void Client::requestNewFortune()
{
getFortuneButton->setEnabled(false);
blockSize = 0;
tcpSocket->abort();
tcpSocket->connectToHost(hostLineEdit->text(), portLineEdit->text().toInt());
}


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

void QAbstractSocket::connectToHost(const QString &hostName, quint16 port,
OpenMode openMode)
{
QMetaObject::invokeMethod(this, "connectToHostImplementation",
Qt::DirectConnection,
Q_ARG(QString, hostName),
Q_ARG(quint16, port),
Q_ARG(OpenMode, openMode));
}


调用的是QAbstractSocket::connectToHostImplementation()。

void QAbstractSocket::connectToHostImplementation(const QString &hostName, quint16 port,
OpenMode openMode)
{
Q_D(QAbstractSocket);
if (d->state == ConnectedState || d->state == ConnectingState || d->state == ClosingState) {
qWarning("QAbstractSocket::connectToHost() called when already connecting/connected to \"%s\"", qPrintable(hostName));
return;
}

d->hostName = hostName;
d->port = port;
d->state = UnconnectedState;
d->readBuffer.clear();
d->writeBuffer.clear();
d->abortCalled = false;
d->closeCalled = false;
d->pendingClose = false;
d->localPort = 0;
d->peerPort = 0;
d->localAddress.clear();
d->peerAddress.clear();
d->peerName = hostName;
if (d->hostLookupId != -1) {
QHostInfo::abortHostLookup(d->hostLookupId);
d->hostLookupId = -1;
}

#ifndef QT_NO_NETWORKPROXY
// Get the proxy information
d->resolveProxy(hostName, port);
if (d->proxyInUse.type() == QNetworkProxy::DefaultProxy) {
// failed to setup the proxy
d->socketError = QAbstractSocket::UnsupportedSocketOperationError;
setErrorString(QAbstractSocket::tr("Operation on socket is not supported"));
emit error(d->socketError);
return;
}
#endif

if (!d_func()->isBuffered)
openMode |= QAbstractSocket::Unbuffered;
QIODevice::open(openMode);  // ??
d->state = HostLookupState;
emit stateChanged(d->state);

QHostAddress temp;
if (temp.setAddress(hostName)) {
QHostInfo info;
info.setAddresses(QList<QHostAddress>() << temp);
d->_q_startConnecting(info);
#ifndef QT_NO_NETWORKPROXY
} else if (d->proxyInUse.capabilities() & QNetworkProxy::HostNameLookupCapability) {
// the proxy supports connection by name, so use it
d->startConnectingByName(hostName);
return;
#endif
} else {
if (d->threadData->eventDispatcher)
d->hostLookupId = QHostInfo::lookupHost(hostName, this, SLOT(_q_startConnecting(QHostInfo)));
}
}


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

void QAbstractSocketPrivate::_q_connectToNextAddress()
{
Q_Q(QAbstractSocket);
do {
// Check for more pending addresses
if (addresses.isEmpty()) {
state = QAbstractSocket::UnconnectedState;
if (socketEngine) {
if ((socketEngine->error() == QAbstractSocket::UnknownSocketError
) && socketEngine->state() == QAbstractSocket::ConnectingState) {
socketError = QAbstractSocket::ConnectionRefusedError;
q->setErrorString(QAbstractSocket::tr("Connection refused"));
} else {
socketError = socketEngine->error();
q->setErrorString(socketEngine->errorString());
}
} else {
//                socketError = QAbstractSocket::ConnectionRefusedError;
//                q->setErrorString(QAbstractSocket::tr("Connection refused"));
}
emit q->stateChanged(state);
emit q->error(socketError);
return;
}

// Pick the first host address candidate
host = addresses.takeFirst();

#if defined(QT_NO_IPV6)
if (host.protocol() == QAbstractSocket::IPv6Protocol) {
// If we have no IPv6 support, then we will not be able to
// connect. So we just pretend we didn't see this address.
continue;
}
#endif

if (!initSocketLayer(host.protocol())) {
// hope that the next address is better
continue;
}

// Tries to connect to the address. If it succeeds immediately
// (localhost address on BSD or any UDP connect), emit
// connected() and return.
if (socketEngine->connectToHost(host, port)) {
//_q_testConnection();
fetchConnectionParameters();
return;
}

// cache the socket descriptor even if we're not fully connected yet
cachedSocketDescriptor = socketEngine->socketDescriptor();

// Check that we're in delayed connection state. If not, try
// the next address
if (socketEngine->state() != QAbstractSocket::ConnectingState) {
continue;
}

// Start the connect timer.
if (threadData->eventDispatcher) {
if (!connectTimer) {
connectTimer = new QTimer(q);
QObject::connect(connectTimer, SIGNAL(timeout()),
65                                  q, SLOT(_q_abortConnectionAttempt()),
66                                  Qt::DirectConnection);
}
connectTimer->start(QT_CONNECT_TIMEOUT);
}

// Wait for a write notification that will eventually call
// _q_testConnection().
socketEngine->setWriteNotificationEnabled(true);
break;
} while (state != QAbstractSocket::ConnectedState);
}


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

bool QAbstractSocketPrivate::initSocketLayer(QAbstractSocket::NetworkLayerProtocol protocol)
{
#ifdef QT_NO_NETWORKPROXY
// this is here to avoid a duplication of the call to createSocketEngine below
static const QNetworkProxy &proxyInUse = *(QNetworkProxy *)0;
#endif

Q_Q(QAbstractSocket);

resetSocketLayer();
socketEngine = QAbstractSocketEngine::createSocketEngine(q->socketType(), proxyInUse, q);
if (!socketEngine) {
socketError = QAbstractSocket::UnsupportedSocketOperationError;
q->setErrorString(QAbstractSocket::tr("Operation on socket is not supported"));
return false;
}
if (!socketEngine->initialize(q->socketType(), protocol)) {
socketError = socketEngine->error();
q->setErrorString(socketEngine->errorString());
return false;
}

if (threadData->eventDispatcher)
socketEngine->setReceiver(this);

return true;
}

QAbstractSocketEngine *QAbstractSocketEngine::createSocketEngine(QAbstractSocket::SocketType socketType, const QNetworkProxy &proxy, QObject *parent)
{
#ifndef QT_NO_NETWORKPROXY
// proxy type must have been resolved by now
if (proxy.type() == QNetworkProxy::DefaultProxy)
return 0;
#endif

QMutexLocker locker(&socketHandlers()->mutex);
for (int i = 0; i < socketHandlers()->size(); i++) {
if (QAbstractSocketEngine *ret = socketHandlers()->at(i)->createSocketEngine(socketType, proxy, parent))
return ret;
}

return new QNativeSocketEngine(parent);
}


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

bool QNativeSocketEngine::initialize(QAbstractSocket::SocketType socketType, QAbstractSocket::NetworkLayerProtocol protocol)
{
Q_D(QNativeSocketEngine);
if (isValid())
close();

#if defined(QT_NO_IPV6)
if (protocol == QAbstractSocket::IPv6Protocol) {
d->setError(QAbstractSocket::UnsupportedSocketOperationError,
QNativeSocketEnginePrivate::NoIpV6ErrorString);
return false;
}
#endif

// Create the socket
if (!d->createNewSocket(socketType, protocol)) {
return false;
}

// Make the socket nonblocking.
if (!setOption(NonBlockingSocketOption, 1)) {
d->setError(QAbstractSocket::UnsupportedSocketOperationError,
QNativeSocketEnginePrivate::NonBlockingInitFailedErrorString);
close();
return false;
}

// Set the broadcasting flag if it's a UDP socket.
if (socketType == QAbstractSocket::UdpSocket
&& !setOption(BroadcastSocketOption, 1)) {
d->setError(QAbstractSocket::UnsupportedSocketOperationError,
QNativeSocketEnginePrivate::BroadcastingInitFailedErrorString);
close();
return false;
}

// Make sure we receive out-of-band data
if (socketType == QAbstractSocket::TcpSocket
&& !setOption(ReceiveOutOfBandData, 1)) {
qWarning("QNativeSocketEngine::initialize unable to inline out-of-band data");
}

// Set the send and receive buffer sizes to a magic size, found
// most optimal for our platforms.
setReceiveBufferSize(49152);
setSendBufferSize(49152);

d->socketType = socketType;
d->socketProtocol = protocol;
return true;
}


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

2、connect到远程目标

bool QNativeSocketEngine::connectToHost(const QHostAddress &address, quint16 port)
{
Q_D(QNativeSocketEngine);
Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::connectToHost(), false);

#if defined (QT_NO_IPV6)
if (address.protocol() == QAbstractSocket::IPv6Protocol) {
d->setError(QAbstractSocket::UnsupportedSocketOperationError,
QNativeSocketEnginePrivate::NoIpV6ErrorString);
return false;
}
#endif
if (!d->checkProxy(address))
return false;

Q_CHECK_STATES(QNativeSocketEngine::connectToHost(),
QAbstractSocket::UnconnectedState, QAbstractSocket::ConnectingState, false);

d->peerAddress = address;
d->peerPort = port;
bool connected = d->nativeConnect(address, port);
if (connected)
d->fetchConnectionParameters();

return connected;
}


连接相对简单。

3、读取信息

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

qint64 QAbstractSocket::readData(char *data, qint64 maxSize)
{
Q_D(QAbstractSocket);
if (d->socketEngine && !d->socketEngine->isReadNotificationEnabled() && d->socketEngine->isValid())
d->socketEngine->setReadNotificationEnabled(true);

if (!d->isBuffered) {
if (!d->socketEngine)
return -1;          // no socket engine is probably EOF
qint64 readBytes = d->socketEngine->read(data, maxSize);
if (readBytes < 0) {
d->socketError = d->socketEngine->error();
setErrorString(d->socketEngine->errorString());
}
if (!d->socketEngine->isReadNotificationEnabled())
d->socketEngine->setReadNotificationEnabled(true);
return readBytes;
}

if (d->readBuffer.isEmpty())
// if we're still connected, return 0 indicating there may be more data in the future
// if we're not connected, return -1 indicating EOF
return d->state == QAbstractSocket::ConnectedState ? qint64(0) : qint64(-1);

// If readFromSocket() read data, copy it to its destination.
if (maxSize == 1) {
*data = d->readBuffer.getChar();
return 1;
}

qint64 bytesToRead = qMin(qint64(d->readBuffer.size()), maxSize);
qint64 readSoFar = 0;
while (readSoFar < bytesToRead) {
const char *ptr = d->readBuffer.readPointer();
int bytesToReadFromThisBlock = qMin(int(bytesToRead - readSoFar),
d->readBuffer.nextDataBlockSize());
memcpy(data + readSoFar, ptr, bytesToReadFromThisBlock);
readSoFar += bytesToReadFromThisBlock;
d->readBuffer.free(bytesToReadFromThisBlock);
}

return readSoFar;
}


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

qint64 QNativeSocketEngine::read(char *data, qint64 maxSize)
{
Q_D(QNativeSocketEngine);
Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::read(), -1);
Q_CHECK_STATES(QNativeSocketEngine::read(), QAbstractSocket::ConnectedState, QAbstractSocket::BoundState, -1);

qint64 readBytes = d->nativeRead(data, maxSize);

// Handle remote close
if (readBytes == 0 && d->socketType == QAbstractSocket::TcpSocket) {
d->setError(QAbstractSocket::RemoteHostClosedError,
QNativeSocketEnginePrivate::RemoteHostClosedErrorString);
close();
return -1;
}
return readBytes;
}


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

qint64 QNativeSocketEnginePrivate::nativeRead(char *data, qint64 maxLength)
{
qint64 ret = -1;
WSABUF buf;
buf.buf = data;
buf.len = maxLength;
DWORD flags = 0;
DWORD bytesRead = 0;
#if defined(Q_OS_WINCE)
WSASetLastError(0);
#endif
if (::WSARecv(socketDescriptor, &buf, 1, &bytesRead, &flags, 0,0) ==  SOCKET_ERROR) {
int err = WSAGetLastError();
WS_ERROR_DEBUG(err);
switch (err) {
case WSAEWOULDBLOCK:
ret = -2;
break;
case WSAEBADF:
case WSAEINVAL:
setError(QAbstractSocket::NetworkError, ReadErrorString);
break;
case WSAECONNRESET:
case WSAECONNABORTED:
// for tcp sockets this will be handled in QNativeSocketEngine::read
ret = 0;
break;
default:
break;
}
} else {
if (WSAGetLastError() == WSAEWOULDBLOCK)
ret = -2;
else
ret = qint64(bytesRead);
}

return ret;
}


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

4、发送数据

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

qint64 QAbstractSocket::writeData(const char *data, qint64 size)
{
Q_D(QAbstractSocket);
if (d->state == QAbstractSocket::UnconnectedState) {
d->socketError = QAbstractSocket::UnknownSocketError;
setErrorString(tr("Socket is not connected"));
return -1;
}

if (!d->isBuffered) {
qint64 written = d->socketEngine->write(data, size);
if (written < 0) {
d->socketError = d->socketEngine->error();
setErrorString(d->socketEngine->errorString());
} else if (!d->writeBuffer.isEmpty()) {
d->socketEngine->setWriteNotificationEnabled(true);
}
if (written >= 0)
emit bytesWritten(written);
return written;
}

char *ptr = d->writeBuffer.reserve(size);
if (size == 1)
*ptr = *data;
else
memcpy(ptr, data, size);

qint64 written = size;

if (d->socketEngine && !d->writeBuffer.isEmpty())
d->socketEngine->setWriteNotificationEnabled(true);
return written;
}


查看QNativeSocketEngine::write():

qint64 QNativeSocketEngine::write(const char *data, qint64 size)
{
Q_D(QNativeSocketEngine);
Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::write(), -1);
Q_CHECK_STATE(QNativeSocketEngine::write(), QAbstractSocket::ConnectedState, -1);
return d->nativeWrite(data, size);
}

qint64 QNativeSocketEnginePrivate::nativeWrite(const char *data, qint64 len)
{
Q_Q(QNativeSocketEngine);
qint64 ret = 0;
// don't send more than 49152 per call to WSASendTo to avoid getting a WSAENOBUFS
for (;;) {
qint64 bytesToSend = qMin<qint64>(49152, len - ret);
WSABUF buf;
buf.buf = (char*)data + ret;
buf.len = bytesToSend;
DWORD flags = 0;
DWORD bytesWritten = 0;

int socketRet = ::WSASend(socketDescriptor, &buf, 1, &bytesWritten, flags, 0,0);

ret += qint64(bytesWritten);

if (socketRet != SOCKET_ERROR) {
if (ret == len)
break;
else
continue;
} else if (WSAGetLastError() == WSAEWOULDBLOCK) {
break;
} else {
int err = WSAGetLastError();
WS_ERROR_DEBUG(err);
switch (err) {
case WSAECONNRESET:
case WSAECONNABORTED:
ret = -1;
setError(QAbstractSocket::NetworkError, WriteErrorString);
q->close();
break;
default:
break;
}
break;
}
}
return ret;
}


至此分析完毕。

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



QEventDispatcherWin32Private::doWsaAsyncSelect()


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

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


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

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

void QNativeSocketEngine::setWriteNotificationEnabled(bool enable)
{
Q_D(QNativeSocketEngine);
if (d->writeNotifier) {
d->writeNotifier->setEnabled(enable);
} else if (enable && d->threadData->eventDispatcher) {
d->writeNotifier = new QWriteNotifier(d->socketDescriptor, this);
d->writeNotifier->setEnabled(true);
}
}


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

QSocketNotifier::QSocketNotifier(int socket, Type type, QObject *parent)
: QObject(parent)
{
if (socket < 0)
qWarning("QSocketNotifier: Invalid socket specified");
sockfd = socket;
sntype = type;
snenabled = true;

Q_D(QObject);
if (!d->threadData->eventDispatcher) {
qWarning("QSocketNotifier: Can only be used with threads started with QThread");
} else {
d->threadData->eventDispatcher->registerSocketNotifier(this);
}
}


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

void QEventDispatcherWin32::registerSocketNotifier(QSocketNotifier *notifier)
{
Q_ASSERT(notifier);
int sockfd = notifier->socket();
int type = notifier->type();

Q_D(QEventDispatcherWin32);
QSNDict *sn_vec[3] = { &d->sn_read, &d->sn_write, &d->sn_except };
QSNDict *dict = sn_vec[type];

if (QCoreApplication::closingDown()) // ### d->exitloop?
return; // after sn_cleanup, don't reinitialize.

if (dict->contains(sockfd)) {
const char *t[] = { "Read", "Write", "Exception" };
/* Variable "socket" below is a function pointer. */
qWarning("QSocketNotifier: Multiple socket notifiers for "
"same socket %d and type %s", sockfd, t[type]);
}

QSockNot *sn = new QSockNot;
sn->obj = notifier;
sn->fd  = sockfd;
dict->insert(sn->fd, sn);

if (d->internalHwnd)
d->doWsaAsyncSelect(sockfd);
}


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

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

void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair)
{
QHttpNetworkRequest &request = messagePair.first;
QHttpNetworkReply *reply = messagePair.second;

// add missing fields for the request
QByteArray value;
// check if Content-Length is provided
QIODevice *data = request.data();
if (data && request.contentLength() == -1) {
if (!data->isSequential())
request.setContentLength(data->size());
else
bufferData(messagePair); // ### or do chunked upload
}
// set the Connection/Proxy-Connection: Keep-Alive headers
#ifndef QT_NO_NETWORKPROXY
if (networkProxy.type() == QNetworkProxy::HttpCachingProxy)  {
value = request.headerField("proxy-connection");
if (value.isEmpty())
request.setHeaderField("Proxy-Connection", "Keep-Alive");
} else {
#endif
value = request.headerField("connection");
if (value.isEmpty())
request.setHeaderField("Connection", "Keep-Alive");
#ifndef QT_NO_NETWORKPROXY
}
#endif

// If the request had a accept-encoding set, we better not mess
// with it. If it was not set, we announce that we understand gzip
// and remember this fact in request.d->autoDecompress so that
// we can later decompress the HTTP reply if it has such an
// encoding.
value = request.headerField("accept-encoding");
if (value.isEmpty()) {
#ifndef QT_NO_COMPRESS
request.setHeaderField("Accept-Encoding", "gzip");
request.d->autoDecompress = true;
#else
// if zlib is not available set this to false always
request.d->autoDecompress = false;
#endif
}
// set the User Agent
value = request.headerField("user-agent");
if (value.isEmpty())
request.setHeaderField("User-Agent", "Mozilla/5.0");
// set the host
value = request.headerField("host");
if (value.isEmpty()) {
QByteArray host = QUrl::toAce(hostName);

int port = request.url().port();
if (port != -1) {
host += ':';
host += QByteArray::number(port);
}

request.setHeaderField("Host", host);
}

reply->d_func()->requestIsPrepared = true;
}


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

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: