📄 q3socket.cpp
字号:
/******************************************************************************** Copyright (C) 1992-2007 Trolltech ASA. All rights reserved.**** This file is part of the Qt3Support module of the Qt Toolkit.**** This file may be used under the terms of the GNU General Public** License version 2.0 as published by the Free Software Foundation** and appearing in the file LICENSE.GPL included in the packaging of** this file. Please review the following information to ensure GNU** General Public Licensing requirements will be met:** http://trolltech.com/products/qt/licenses/licensing/opensource/**** If you are unsure which license is appropriate for your use, please** review the following information:** http://trolltech.com/products/qt/licenses/licensing/licensingoverview** or contact the sales department at sales@trolltech.com.**** In addition, as a special exception, Trolltech gives you certain** additional rights. These rights are described in the Trolltech GPL** Exception version 1.0, which can be found at** http://www.trolltech.com/products/qt/gplexception/ and in the file** GPL_EXCEPTION.txt in this package.**** In addition, as a special exception, Trolltech, as the sole copyright** holder for Qt Designer, grants users of the Qt/Eclipse Integration** plug-in the right for the Qt/Eclipse Integration to link to** functionality provided by Qt Designer and its related libraries.**** Trolltech reserves all rights not expressly granted herein.**** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.******************************************************************************/#include "q3socket.h"#ifndef QT_NO_NETWORK#include "q3ptrlist.h"#include "qtimer.h"#include "q3socketdevice.h"#include "q3dns.h"#include "private/q3membuf_p.h"#include <string.h>#ifndef NO_ERRNO_H#include <errno.h>#endif//#define Q3SOCKET_DEBUG/* Perhaps this private functionality needs to be refactored. Comment from Robert D Gatlin (Intel): It would be nice to have the functionality inherent in Q3Socket available as a separate class as a standard part of the Qt library, something along the line of: class QByteBuffer : public QIODevice { ... } The same class could/would be used within Q3Socket for the Read/Write buffers. The above class could be used in the following way(s): buffer.open( IO_WriteOnly | IO_Append ); buffer.writeBlock( a ); // a = QByteArray buffer.close(); QByteArray b; b.resize( buffer.size() ); buffer.open( IO_ReadOnly ); buffer.readBlock( b.data(), b.size() ); buffer.close(); But would also be useable with QDataStream (via QIODevice) with: buffer.open( IO_WriteOnly | IO_Append ); QDataStream is( &buffer ); is << 100; buffer.close(); buffer.open( IO_ReadOnly ); QDataStream os( &buffer ); Q_UINT32 x; os >> x; buffer.close(); The real usefulness is with any situations where data (QByteArray) arrives incrementally (as in Q3Socket and filter case above). I tried using QBuffer, but QBuffer does not trim bytes from the front of the buffer in cases like: QBuffer buf; buf.open( IO_ReadOnly ); QDataStream ds( &buf ); Q_INT32 x; ds >> x; buf.close(); In the above case, buf.size() will be identical before and after the operation with QDataStream. Based on the implementation of QBuffer, it does not appear well suited for this kind of operation.*/// Private class for Q3Socketclass Q3SocketPrivate {public: Q3SocketPrivate(); ~Q3SocketPrivate(); void closeSocket(); void close(); void connectionClosed(); void setSocketDevice( Q3Socket *q, Q3SocketDevice *device ); Q3Socket::State state; // connection state QString host; // host name Q_UINT16 port; // host port Q3SocketDevice *socket; // connection socket QSocketNotifier *rsn, *wsn; // socket notifiers Q3Membuf rba; // read buffer Q_ULONG readBufferSize; // limit for the read buffer size Q3PtrList<QByteArray> wba; // list of write bufs QHostAddress addr; // connection address Q3ValueList<QHostAddress> addresses; // alternatives looked up QIODevice::Offset wsize; // write total buf size QIODevice::Offset windex; // write index#ifndef QT_NO_DNS Q3Dns *dns4; Q3Dns *dns6;#endif static Q3PtrList<Q3Socket> sn_read_alreadyCalled; // used to avoid unwanted recursion Q3ValueList<QHostAddress> l4; Q3ValueList<QHostAddress> l6;};Q3PtrList<Q3Socket> Q3SocketPrivate::sn_read_alreadyCalled;Q3SocketPrivate::Q3SocketPrivate() : state(Q3Socket::Idle), host(QString::fromLatin1("")), port(0), socket(0), rsn(0), wsn(0), readBufferSize(0), wsize(0), windex(0){#ifndef QT_NO_DNS dns4 = 0; dns6 = 0;#endif wba.setAutoDelete( true );}Q3SocketPrivate::~Q3SocketPrivate(){ close(); delete socket;#ifndef QT_NO_DNS delete dns4; delete dns6;#endif}void Q3SocketPrivate::closeSocket(){ // Order is important here - the socket notifiers must go away // before the socket does, otherwise libc or the kernel will // become unhappy. delete rsn; rsn = 0; delete wsn; wsn = 0; if ( socket ) socket->close();}void Q3SocketPrivate::close(){ closeSocket(); wsize = 0; rba.clear(); wba.clear(); windex = 0;}void Q3SocketPrivate::connectionClosed(){ // We keep the open state in case there's unread incoming data state = Q3Socket::Idle; closeSocket(); wba.clear(); windex = wsize = 0;}void Q3SocketPrivate::setSocketDevice( Q3Socket *q, Q3SocketDevice *device ){ delete socket; delete rsn; delete wsn; if ( device ) { socket = device; } else { socket = new Q3SocketDevice( Q3SocketDevice::Stream, ( addr.isIPv4Address() ? Q3SocketDevice::IPv4 : Q3SocketDevice::IPv6 ), 0 ); socket->setBlocking( false ); socket->setAddressReusable( true ); } rsn = new QSocketNotifier( socket->socket(), QSocketNotifier::Read, q, "read" ); wsn = new QSocketNotifier( socket->socket(), QSocketNotifier::Write, q, "write" ); QObject::connect( rsn, SIGNAL(activated(int)), q, SLOT(sn_read()) ); rsn->setEnabled( false ); QObject::connect( wsn, SIGNAL(activated(int)), q, SLOT(sn_write()) ); wsn->setEnabled( false );}/*! \class Q3Socket q3socket.h \brief The Q3Socket class provides a buffered TCP connection. \compat It provides a totally non-blocking QIODevice, and modifies and extends the API of QIODevice with socket-specific code. The functions you're likely to call most are connectToHost(), bytesAvailable(), canReadLine() and the ones it inherits from QIODevice. connectToHost() is the most-used function. As its name implies, it opens a connection to a named host. Most network protocols are either packet-oriented or line-oriented. canReadLine() indicates whether a connection contains an entire unread line or not, and bytesAvailable() returns the number of bytes available for reading. The signals error(), connected(), readyRead() and connectionClosed() inform you of the progress of the connection. There are also some less commonly used signals. hostFound() is emitted when connectToHost() has finished its DNS lookup and is starting its TCP connection. delayedCloseFinished() is emitted when close() succeeds. bytesWritten() is emitted when Q3Socket moves data from its "to be written" queue into the TCP implementation. There are several access functions for the socket: state() returns whether the object is idle, is doing a DNS lookup, is connecting, has an operational connection, etc. address() and port() return the IP address and port used for the connection. The peerAddress() and peerPort() functions return the IP address and port used by the peer, and peerName() returns the name of the peer (normally the name that was passed to connectToHost()). socketDevice() returns a pointer to the Q3SocketDevice used for this socket. Q3Socket inherits QIODevice, and reimplements some functions. In general, you can treat it as a QIODevice for writing, and mostly also for reading. The match isn't perfect, since the QIODevice API is designed for devices that are controlled by the same machine, and an asynchronous peer-to-peer network connection isn't quite like that. For example, there is nothing that matches QIODevice::size() exactly. The documentation for open(), close(), flush(), size(), at(), atEnd(), readBlock(), writeBlock(), getch(), putch(), ungetch() and readLine() describes the differences in detail. \warning Q3Socket is not suitable for use in threads. If you need to uses sockets in threads use the lower-level Q3SocketDevice class. \sa Q3SocketDevice, QHostAddress, QSocketNotifier*//*! Creates a Q3Socket object in Q3Socket::Idle state. The \a parent and \a name arguments are passed on to the QObject constructor.*/Q3Socket::Q3Socket( QObject *parent, const char *name ) : QIODevice( parent ){ setObjectName(QLatin1String(name)); d = new Q3SocketPrivate; setSocketDevice( 0 ); resetStatus();}/*! Destroys the socket. Closes the connection if necessary. \sa close()*/Q3Socket::~Q3Socket(){#if defined(Q3SOCKET_DEBUG) qDebug( "Q3Socket (%s): Destroy", name() );#endif if ( state() != Idle ) close(); Q_ASSERT( d != 0 ); delete d;}/*! Returns a pointer to the internal socket device. There is normally no need to manipulate the socket device directly since this class does the necessary setup for most applications.*/Q3SocketDevice *Q3Socket::socketDevice(){ return d->socket;}/*! Sets the internal socket device to \a device. Passing a \a device of 0 will cause the internal socket device to be used. Any existing connection will be disconnected before using the new \a device. The new device should not be connected before being associated with a Q3Socket; after setting the socket call connectToHost() to make the connection. This function is useful if you need to subclass Q3SocketDevice and want to use the Q3Socket API, for example, to implement Unix domain sockets.*/void Q3Socket::setSocketDevice( Q3SocketDevice *device ){ if ( state() != Idle ) close(); d->setSocketDevice( this, device );}/*! \enum Q3Socket::State This enum defines the connection states: \value Idle if there is no connection \value HostLookup during a DNS lookup \value Connecting during TCP connection establishment \value Connected when there is an operational connection \value Closing if the socket is closing down, but is not yet closed. \omitvalue Connection*//*! Returns the current state of the socket connection. \sa Q3Socket::State*/Q3Socket::State Q3Socket::state() const{ return d->state;}#ifndef QT_NO_DNS/*! Attempts to make a connection to \a host on the specified \a port and return immediately. Any connection or pending connection is closed immediately, and Q3Socket goes into the \c HostLookup state. When the lookup succeeds, it emits hostFound(), starts a TCP connection and goes into the \c Connecting state. Finally, when the connection succeeds, it emits connected() and goes into the \c Connected state. If there is an error at any point, it emits error(). \a host may be an IP address in string form, or it may be a DNS name. Q3Socket will do a normal DNS lookup if required. Note that \a port is in native byte order, unlike some other libraries. \sa state()*/void Q3Socket::connectToHost( const QString &host, Q_UINT16 port ){#if defined(Q3SOCKET_DEBUG) qDebug( "Q3Socket (%s)::connectToHost: host %s, port %d", name(), host.ascii(), port );#endif setSocketIntern( -1 ); d->state = HostLookup; d->host = host; d->port = port; d->dns4 = new Q3Dns( host, Q3Dns::A ); d->dns6 = new Q3Dns( host, Q3Dns::Aaaa ); // try if the address is already available (for faster connecting...) tryConnecting(); if ( d->state == HostLookup ) { connect( d->dns4, SIGNAL(resultsReady()), this, SLOT(tryConnecting()) ); connect( d->dns6, SIGNAL(resultsReady()), this, SLOT(tryConnecting()) ); }}#endif/*! This private slots continues the connection process where connectToHost() leaves off.*/void Q3Socket::tryConnecting(){#if defined(Q3SOCKET_DEBUG) qDebug( "Q3Socket (%s)::tryConnecting()", name() );#endif // ### this ifdef isn't correct - addresses() also does /etc/hosts and // numeric-address-as-string handling.#ifndef QT_NO_DNS if ( d->dns4 ) { d->l4 = d->dns4->addresses(); if ( !d->l4.isEmpty() || !d->dns4->isWorking() ) {#if defined(Q3SOCKET_DEBUG) qDebug( "Q3Socket (%s)::tryConnecting: host %s, port %d: " "%d IPv4 addresses", name(), d->host.ascii(), d->port, d->l4.count() );#endif delete d->dns4; d->dns4 = 0; } } if ( d->dns6 ) { d->l6 = d->dns6->addresses(); if ( !d->l6.isEmpty() || !d->dns6->isWorking() ) {#if defined(Q3SOCKET_DEBUG) qDebug( "Q3Socket (%s)::tryConnecting: host %s, port %d: " "%d IPv6 addresses", name(), d->host.ascii(), d->port, d->l6.count() );#endif delete d->dns6; d->dns6 = 0; } } if ( d->state == HostLookup ) { if ( d->l4.isEmpty() && d->l6.isEmpty() && !d->dns4 && !d->dns6 ) { // no results and we're not still looking: give up d->state = Idle; emit error( ErrHostNotFound ); return; } if ( d->l4.isEmpty() && d->l6.isEmpty() ) { // no results (yet): try again later return; } // we've found something. press on with that. if we later find // more, fine. emit hostFound(); d->state = Connecting; } if ( d->state == Connecting ) { d->addresses += d->l4; d->addresses += d->l6; d->l4.clear(); d->l6.clear(); // try one address at a time, falling back to the next one if // there is a connection failure. (should also support a timeout, // or do multiple TCP-level connects at a time, with staggered // starts to avoid bandwidth waste and cause fewer // "connect-and-abort" errors. but that later.) bool stuck = true; while( stuck ) { stuck = false; if ( d->socket && d->socket->connect( d->addr, d->port ) == false ) { if ( d->socket->error() == Q3SocketDevice::NoError ) { if ( d->wsn ) d->wsn->setEnabled( true ); return; // not serious, try again later }#if defined(Q3SOCKET_DEBUG) qDebug( "Q3Socket (%s)::tryConnecting: " "Gave up on IP address %s",
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -