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

QT分析之网络编程(二)

2013-03-30 10:52 465 查看
前面分析(一)之前没有看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()),

q, SLOT(_q_abortConnectionAttempt()),

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;

}

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