前面分析(一)之前没有看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 da
if (socketType == QAbstractSocket::TcpSocket
&& !setOption(ReceiveOutOfBandData, 1)) {
qWarning("QNativeSocketEngine::initialize unable to inline out-of-band da
}
// 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;
}
连接相对简单。
QT分析之网络编程(二)
最新推荐文章于 2024-04-08 16:33:54 发布
原文链接:
http://blog.163.com/net_worm/blog/static/127702419201003111842804/