/src/qt/qtbase/src/network/socket/qabstractsocket.cpp
C++ | 3003 lines | 1595 code | 275 blank | 1133 comment | 401 complexity | c21b748f31677e371bdb50995a6dafef MD5 | raw file
Possible License(s): LGPL-3.0, CC-BY-SA-4.0, MIT, AGPL-3.0, BSD-3-Clause, LGPL-2.1, CC0-1.0, GPL-2.0, LGPL-2.0, GPL-3.0
Large files files are truncated, but you can click here to view the full file
- /****************************************************************************
- **
- ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
- ** Contact: http://www.qt-project.org/legal
- **
- ** This file is part of the QtNetwork module of the Qt Toolkit.
- **
- ** $QT_BEGIN_LICENSE:LGPL$
- ** Commercial License Usage
- ** Licensees holding valid commercial Qt licenses may use this file in
- ** accordance with the commercial license agreement provided with the
- ** Software or, alternatively, in accordance with the terms contained in
- ** a written agreement between you and Digia. For licensing terms and
- ** conditions see http://qt.digia.com/licensing. For further information
- ** use the contact form at http://qt.digia.com/contact-us.
- **
- ** GNU Lesser General Public License Usage
- ** Alternatively, this file may be used under the terms of the GNU Lesser
- ** General Public License version 2.1 as published by the Free Software
- ** Foundation and appearing in the file LICENSE.LGPL included in the
- ** packaging of this file. Please review the following information to
- ** ensure the GNU Lesser General Public License version 2.1 requirements
- ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
- **
- ** In addition, as a special exception, Digia gives you certain additional
- ** rights. These rights are described in the Digia Qt LGPL Exception
- ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
- **
- ** GNU General Public License Usage
- ** Alternatively, this file may be used under the terms of the GNU
- ** General Public License version 3.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 the GNU General Public License version 3.0 requirements will be
- ** met: http://www.gnu.org/copyleft/gpl.html.
- **
- **
- ** $QT_END_LICENSE$
- **
- ****************************************************************************/
- //#define QABSTRACTSOCKET_DEBUG
- /*!
- \class QAbstractSocket
- \brief The QAbstractSocket class provides the base functionality
- common to all socket types.
- \reentrant
- \ingroup network
- \inmodule QtNetwork
- QAbstractSocket is the base class for QTcpSocket and QUdpSocket
- and contains all common functionality of these two classes. If
- you need a socket, you have two options:
- \list
- \li Instantiate QTcpSocket or QUdpSocket.
- \li Create a native socket descriptor, instantiate
- QAbstractSocket, and call setSocketDescriptor() to wrap the
- native socket.
- \endlist
- TCP (Transmission Control Protocol) is a reliable,
- stream-oriented, connection-oriented transport protocol. UDP
- (User Datagram Protocol) is an unreliable, datagram-oriented,
- connectionless protocol. In practice, this means that TCP is
- better suited for continuous transmission of data, whereas the
- more lightweight UDP can be used when reliability isn't
- important.
- QAbstractSocket's API unifies most of the differences between the
- two protocols. For example, although UDP is connectionless,
- connectToHost() establishes a virtual connection for UDP sockets,
- enabling you to use QAbstractSocket in more or less the same way
- regardless of the underlying protocol. Internally,
- QAbstractSocket remembers the address and port passed to
- connectToHost(), and functions like read() and write() use these
- values.
- At any time, QAbstractSocket has a state (returned by
- state()). The initial state is UnconnectedState. After
- calling connectToHost(), the socket first enters
- HostLookupState. If the host is found, QAbstractSocket enters
- ConnectingState and emits the hostFound() signal. When the
- connection has been established, it enters ConnectedState and
- emits connected(). If an error occurs at any stage, error() is
- emitted. Whenever the state changes, stateChanged() is emitted.
- For convenience, isValid() returns \c true if the socket is ready for
- reading and writing, but note that the socket's state must be
- ConnectedState before reading and writing can occur.
- Read or write data by calling read() or write(), or use the
- convenience functions readLine() and readAll(). QAbstractSocket
- also inherits getChar(), putChar(), and ungetChar() from
- QIODevice, which work on single bytes. The bytesWritten() signal
- is emitted when data has been written to the socket. Note that Qt does
- not limit the write buffer size. You can monitor its size by listening
- to this signal.
- The readyRead() signal is emitted every time a new chunk of data
- has arrived. bytesAvailable() then returns the number of bytes
- that are available for reading. Typically, you would connect the
- readyRead() signal to a slot and read all available data there.
- If you don't read all the data at once, the remaining data will
- still be available later, and any new incoming data will be
- appended to QAbstractSocket's internal read buffer. To limit the
- size of the read buffer, call setReadBufferSize().
- To close the socket, call disconnectFromHost(). QAbstractSocket enters
- QAbstractSocket::ClosingState. After all pending data has been written to
- the socket, QAbstractSocket actually closes the socket, enters
- QAbstractSocket::ClosedState, and emits disconnected(). If you want to
- abort a connection immediately, discarding all pending data, call abort()
- instead. If the remote host closes the connection, QAbstractSocket will
- emit error(QAbstractSocket::RemoteHostClosedError), during which the socket
- state will still be ConnectedState, and then the disconnected() signal
- will be emitted.
- The port and address of the connected peer is fetched by calling
- peerPort() and peerAddress(). peerName() returns the host name of
- the peer, as passed to connectToHost(). localPort() and
- localAddress() return the port and address of the local socket.
- QAbstractSocket provides a set of functions that suspend the
- calling thread until certain signals are emitted. These functions
- can be used to implement blocking sockets:
- \list
- \li waitForConnected() blocks until a connection has been established.
- \li waitForReadyRead() blocks until new data is available for
- reading.
- \li waitForBytesWritten() blocks until one payload of data has been
- written to the socket.
- \li waitForDisconnected() blocks until the connection has closed.
- \endlist
- We show an example:
- \snippet network/tcpwait.cpp 0
- If \l{QIODevice::}{waitForReadyRead()} returns \c false, the
- connection has been closed or an error has occurred.
- Programming with a blocking socket is radically different from
- programming with a non-blocking socket. A blocking socket doesn't
- require an event loop and typically leads to simpler code.
- However, in a GUI application, blocking sockets should only be
- used in non-GUI threads, to avoid freezing the user interface.
- See the \l fortuneclient and \l blockingfortuneclient
- examples for an overview of both approaches.
- \note We discourage the use of the blocking functions together
- with signals. One of the two possibilities should be used.
- QAbstractSocket can be used with QTextStream and QDataStream's
- stream operators (operator<<() and operator>>()). There is one
- issue to be aware of, though: You must make sure that enough data
- is available before attempting to read it using operator>>().
- \sa QNetworkAccessManager, QTcpServer
- */
- /*!
- \fn void QAbstractSocket::hostFound()
- This signal is emitted after connectToHost() has been called and
- the host lookup has succeeded.
- \note Since Qt 4.6.3 QAbstractSocket may emit hostFound()
- directly from the connectToHost() call since a DNS result could have been
- cached.
- \sa connected()
- */
- /*!
- \fn void QAbstractSocket::connected()
- This signal is emitted after connectToHost() has been called and
- a connection has been successfully established.
- \note On some operating systems the connected() signal may
- be directly emitted from the connectToHost() call for connections
- to the localhost.
- \sa connectToHost(), disconnected()
- */
- /*!
- \fn void QAbstractSocket::disconnected()
- This signal is emitted when the socket has been disconnected.
- \warning If you need to delete the sender() of this signal in a slot connected
- to it, use the \l{QObject::deleteLater()}{deleteLater()} function.
- \sa connectToHost(), disconnectFromHost(), abort()
- */
- /*!
- \fn void QAbstractSocket::error(QAbstractSocket::SocketError socketError)
- This signal is emitted after an error occurred. The \a socketError
- parameter describes the type of error that occurred.
- When this signal is emitted, the socket may not be ready for a reconnect
- attempt. In that case, attempts to reconnect should be done from the event
- loop. For example, use a QTimer::singleShot() with 0 as the timeout.
- QAbstractSocket::SocketError is not a registered metatype, so for queued
- connections, you will have to register it with Q_DECLARE_METATYPE() and
- qRegisterMetaType().
- \sa error(), errorString(), {Creating Custom Qt Types}
- */
- /*!
- \fn void QAbstractSocket::stateChanged(QAbstractSocket::SocketState socketState)
- This signal is emitted whenever QAbstractSocket's state changes.
- The \a socketState parameter is the new state.
- QAbstractSocket::SocketState is not a registered metatype, so for queued
- connections, you will have to register it with Q_DECLARE_METATYPE() and
- qRegisterMetaType().
- \sa state(), {Creating Custom Qt Types}
- */
- /*!
- \fn void QAbstractSocket::proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator)
- \since 4.3
- This signal can be emitted when a \a proxy that requires
- authentication is used. The \a authenticator object can then be
- filled in with the required details to allow authentication and
- continue the connection.
- \note It is not possible to use a QueuedConnection to connect to
- this signal, as the connection will fail if the authenticator has
- not been filled in with new information when the signal returns.
- \sa QAuthenticator, QNetworkProxy
- */
- /*!
- \enum QAbstractSocket::NetworkLayerProtocol
- This enum describes the network layer protocol values used in Qt.
- \value IPv4Protocol IPv4
- \value IPv6Protocol IPv6
- \value AnyIPProtocol Either IPv4 or IPv6
- \value UnknownNetworkLayerProtocol Other than IPv4 and IPv6
- \sa QHostAddress::protocol()
- */
- /*!
- \enum QAbstractSocket::SocketType
- This enum describes the transport layer protocol.
- \value TcpSocket TCP
- \value UdpSocket UDP
- \value UnknownSocketType Other than TCP and UDP
- \sa QAbstractSocket::socketType()
- */
- /*!
- \enum QAbstractSocket::SocketError
- This enum describes the socket errors that can occur.
- \value ConnectionRefusedError The connection was refused by the
- peer (or timed out).
- \value RemoteHostClosedError The remote host closed the
- connection. Note that the client socket (i.e., this socket)
- will be closed after the remote close notification has
- been sent.
- \value HostNotFoundError The host address was not found.
- \value SocketAccessError The socket operation failed because the
- application lacked the required privileges.
- \value SocketResourceError The local system ran out of resources
- (e.g., too many sockets).
- \value SocketTimeoutError The socket operation timed out.
- \value DatagramTooLargeError The datagram was larger than the
- operating system's limit (which can be as low as 8192
- bytes).
- \value NetworkError An error occurred with the network (e.g., the
- network cable was accidentally plugged out).
- \value AddressInUseError The address specified to QAbstractSocket::bind() is
- already in use and was set to be exclusive.
- \value SocketAddressNotAvailableError The address specified to
- QAbstractSocket::bind() does not belong to the host.
- \value UnsupportedSocketOperationError The requested socket operation is
- not supported by the local operating system (e.g., lack of
- IPv6 support).
- \value ProxyAuthenticationRequiredError The socket is using a proxy, and
- the proxy requires authentication.
- \value SslHandshakeFailedError The SSL/TLS handshake failed, so
- the connection was closed (only used in QSslSocket)
- \value UnfinishedSocketOperationError Used by QAbstractSocketEngine only,
- The last operation attempted has not finished yet (still in progress in
- the background).
- \value ProxyConnectionRefusedError Could not contact the proxy server because
- the connection to that server was denied
- \value ProxyConnectionClosedError The connection to the proxy server was closed
- unexpectedly (before the connection to the final peer was established)
- \value ProxyConnectionTimeoutError The connection to the proxy server timed out
- or the proxy server stopped responding in the authentication phase.
- \value ProxyNotFoundError The proxy address set with setProxy() (or the application
- proxy) was not found.
- \value ProxyProtocolError The connection negotiation with the proxy server failed,
- because the response from the proxy server could not be understood.
- \value OperationError An operation was attempted while the socket was in a state that
- did not permit it.
- \value SslInternalError The SSL library being used reported an internal error. This is
- probably the result of a bad installation or misconfiguration of the library.
- \value SslInvalidUserDataError Invalid data (certificate, key, cypher, etc.) was
- provided and its use resulted in an error in the SSL library.
- \value TemporaryError A temporary error occurred (e.g., operation would block and socket
- is non-blocking).
- \value UnknownSocketError An unidentified error occurred.
- \sa QAbstractSocket::error()
- */
- /*!
- \enum QAbstractSocket::SocketState
- This enum describes the different states in which a socket can be.
- \value UnconnectedState The socket is not connected.
- \value HostLookupState The socket is performing a host name lookup.
- \value ConnectingState The socket has started establishing a connection.
- \value ConnectedState A connection is established.
- \value BoundState The socket is bound to an address and port.
- \value ClosingState The socket is about to close (data may still
- be waiting to be written).
- \value ListeningState For internal use only.
- \sa QAbstractSocket::state()
- */
- /*!
- \enum QAbstractSocket::SocketOption
- \since 4.6
- This enum represents the options that can be set on a socket. If
- desired, they can be set after having received the connected()
- signal from the socket or after having received a new socket from
- a QTcpServer.
- \value LowDelayOption Try to optimize the socket for low
- latency. For a QTcpSocket this would set the TCP_NODELAY option
- and disable Nagle's algorithm. Set this to 1 to enable.
- \value KeepAliveOption Set this to 1 to enable the SO_KEEPALIVE
- socket option
- \value MulticastTtlOption Set this to an integer value to set
- IP_MULTICAST_TTL (TTL for multicast datagrams) socket option.
- \value MulticastLoopbackOption Set this to 1 to enable the
- IP_MULTICAST_LOOP (multicast loopback) socket option.
- \value TypeOfServiceOption This option is not supported on
- Windows. This maps to the IP_TOS socket option. For possible values,
- see table below.
- \value SendBufferSizeSocketOption Sets the socket send buffer size
- in bytes at the OS level. This maps to the SO_SNDBUF socket option.
- This option does not affect the QIODevice or QAbstractSocket buffers.
- This enum value has been introduced in Qt 5.3.
- \value ReceiveBufferSizeSocketOption Sets the socket receive
- buffer size in bytes at the OS level.
- This maps to the SO_RCVBUF socket option.
- This option does not affect the QIODevice or QAbstractSocket buffers
- (see \l{QAbstractSocket::}{setReadBufferSize()}).
- This enum value has been introduced in Qt 5.3.
- Possible values for \e{TypeOfServiceOption} are:
- \table
- \header \li Value \li Description
- \row \li 224 \li Network control
- \row \li 192 \li Internetwork control
- \row \li 160 \li CRITIC/ECP
- \row \li 128 \li Flash override
- \row \li 96 \li Flash
- \row \li 64 \li Immediate
- \row \li 32 \li Priority
- \row \li 0 \li Routine
- \endtable
- \sa QAbstractSocket::setSocketOption(), QAbstractSocket::socketOption()
- */
- /*! \enum QAbstractSocket::BindFlag
- \since 5.0
- This enum describes the different flags you can pass to modify the
- behavior of QAbstractSocket::bind().
- \value ShareAddress Allow other services to bind to the same address
- and port. This is useful when multiple processes share
- the load of a single service by listening to the same address and port
- (e.g., a web server with several pre-forked listeners can greatly
- improve response time). However, because any service is allowed to
- rebind, this option is subject to certain security considerations.
- Note that by combining this option with ReuseAddressHint, you will
- also allow your service to rebind an existing shared address. On
- Unix, this is equivalent to the SO_REUSEADDR socket option. On Windows,
- this option is ignored.
- \value DontShareAddress Bind the address and port exclusively, so that
- no other services are allowed to rebind. By passing this option to
- QAbstractSocket::bind(), you are guaranteed that on successs, your service
- is the only one that listens to the address and port. No services are
- allowed to rebind, even if they pass ReuseAddressHint. This option
- provides more security than ShareAddress, but on certain operating
- systems, it requires you to run the server with administrator privileges.
- On Unix and Mac OS X, not sharing is the default behavior for binding
- an address and port, so this option is ignored. On Windows, this
- option uses the SO_EXCLUSIVEADDRUSE socket option.
- \value ReuseAddressHint Provides a hint to QAbstractSocket that it should try
- to rebind the service even if the address and port are already bound by
- another socket. On Windows, this is equivalent to the SO_REUSEADDR
- socket option. On Unix, this option is ignored.
- \value DefaultForPlatform The default option for the current platform.
- On Unix and Mac OS X, this is equivalent to (DontShareAddress
- + ReuseAddressHint), and on Windows, its equivalent to ShareAddress.
- */
- /*! \enum QAbstractSocket::PauseMode
- \since 5.0
- This enum describes the behavior of when the socket should hold
- back with continuing data transfer.
- The only notification currently supported is QSslSocket::sslErrors().
- \value PauseNever Do not pause data transfer on the socket. This is the
- default and matches the behaviour of Qt 4.
- \value PauseOnSslErrors Pause data transfer on the socket upon receiving an
- SSL error notification. I.E. QSslSocket::sslErrors().
- */
- #include "qabstractsocket.h"
- #include "qabstractsocket_p.h"
- #include "private/qhostinfo_p.h"
- #include "private/qnetworksession_p.h"
- #include <qabstracteventdispatcher.h>
- #include <qhostaddress.h>
- #include <qhostinfo.h>
- #include <qmetaobject.h>
- #include <qpointer.h>
- #include <qtimer.h>
- #include <qelapsedtimer.h>
- #include <qscopedvaluerollback.h>
- #ifndef QT_NO_SSL
- #include <QtNetwork/qsslsocket.h>
- #endif
- #include <private/qthread_p.h>
- #ifdef QABSTRACTSOCKET_DEBUG
- #include <qdebug.h>
- #endif
- #include <time.h>
- #define Q_CHECK_SOCKETENGINE(returnValue) do { \
- if (!d->socketEngine) { \
- return returnValue; \
- } } while (0)
- #ifndef QABSTRACTSOCKET_BUFFERSIZE
- #define QABSTRACTSOCKET_BUFFERSIZE 32768
- #endif
- #define QT_CONNECT_TIMEOUT 30000
- #define QT_TRANSFER_TIMEOUT 120000
- QT_BEGIN_NAMESPACE
- #if defined QABSTRACTSOCKET_DEBUG
- QT_BEGIN_INCLUDE_NAMESPACE
- #include <qstring.h>
- #include <ctype.h>
- QT_END_INCLUDE_NAMESPACE
- /*
- Returns a human readable representation of the first \a len
- characters in \a data.
- */
- static QByteArray qt_prettyDebug(const char *data, int len, int maxLength)
- {
- if (!data) return "(null)";
- QByteArray out;
- for (int i = 0; i < len; ++i) {
- char c = data[i];
- if (isprint(int(uchar(c)))) {
- out += c;
- } else switch (c) {
- case '\n': out += "\\n"; break;
- case '\r': out += "\\r"; break;
- case '\t': out += "\\t"; break;
- default:
- QString tmp;
- tmp.sprintf("\\%o", c);
- out += tmp.toLatin1();
- }
- }
- if (len < maxLength)
- out += "...";
- return out;
- }
- #endif
- static bool isProxyError(QAbstractSocket::SocketError error)
- {
- switch (error) {
- case QAbstractSocket::ProxyAuthenticationRequiredError:
- case QAbstractSocket::ProxyConnectionRefusedError:
- case QAbstractSocket::ProxyConnectionClosedError:
- case QAbstractSocket::ProxyConnectionTimeoutError:
- case QAbstractSocket::ProxyNotFoundError:
- case QAbstractSocket::ProxyProtocolError:
- return true;
- default:
- return false;
- }
- }
- /*! \internal
- Constructs a QAbstractSocketPrivate. Initializes all members.
- */
- QAbstractSocketPrivate::QAbstractSocketPrivate()
- : readSocketNotifierCalled(false),
- readSocketNotifierState(false),
- readSocketNotifierStateSet(false),
- emittedReadyRead(false),
- emittedBytesWritten(false),
- abortCalled(false),
- closeCalled(false),
- pendingClose(false),
- pauseMode(QAbstractSocket::PauseNever),
- port(0),
- localPort(0),
- peerPort(0),
- socketEngine(0),
- cachedSocketDescriptor(-1),
- readBufferMaxSize(0),
- writeBuffer(QABSTRACTSOCKET_BUFFERSIZE),
- isBuffered(false),
- blockingTimeout(30000),
- connectTimer(0),
- disconnectTimer(0),
- connectTimeElapsed(0),
- hostLookupId(-1),
- socketType(QAbstractSocket::UnknownSocketType),
- state(QAbstractSocket::UnconnectedState),
- socketError(QAbstractSocket::UnknownSocketError),
- preferredNetworkLayerProtocol(QAbstractSocket::UnknownNetworkLayerProtocol)
- {
- }
- /*! \internal
- Destructs the QAbstractSocket. If the socket layer is open, it
- will be reset.
- */
- QAbstractSocketPrivate::~QAbstractSocketPrivate()
- {
- }
- /*! \internal
- Resets the socket layer and deletes any socket notifiers.
- */
- void QAbstractSocketPrivate::resetSocketLayer()
- {
- #if defined (QABSTRACTSOCKET_DEBUG)
- qDebug("QAbstractSocketPrivate::resetSocketLayer()");
- #endif
- if (socketEngine) {
- socketEngine->close();
- socketEngine->disconnect();
- delete socketEngine;
- socketEngine = 0;
- cachedSocketDescriptor = -1;
- }
- if (connectTimer)
- connectTimer->stop();
- if (disconnectTimer)
- disconnectTimer->stop();
- }
- /*! \internal
- Initializes the socket layer to by of type \a type, using the
- network layer protocol \a protocol. Resets the socket layer first
- if it's already initialized. Sets up the socket notifiers.
- */
- 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);
- #if defined (QABSTRACTSOCKET_DEBUG)
- QString typeStr;
- if (q->socketType() == QAbstractSocket::TcpSocket) typeStr = QLatin1String("TcpSocket");
- else if (q->socketType() == QAbstractSocket::UdpSocket) typeStr = QLatin1String("UdpSocket");
- else typeStr = QLatin1String("UnknownSocketType");
- QString protocolStr;
- if (protocol == QAbstractSocket::IPv4Protocol) protocolStr = QLatin1String("IPv4Protocol");
- else if (protocol == QAbstractSocket::IPv6Protocol) protocolStr = QLatin1String("IPv6Protocol");
- else protocolStr = QLatin1String("UnknownNetworkLayerProtocol");
- #endif
- 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;
- }
- #ifndef QT_NO_BEARERMANAGEMENT
- //copy network session down to the socket engine (if it has been set)
- socketEngine->setProperty("_q_networksession", q->property("_q_networksession"));
- #endif
- if (!socketEngine->initialize(q->socketType(), protocol)) {
- #if defined (QABSTRACTSOCKET_DEBUG)
- qDebug("QAbstractSocketPrivate::initSocketLayer(%s, %s) failed (%s)",
- typeStr.toLatin1().constData(), protocolStr.toLatin1().constData(),
- socketEngine->errorString().toLatin1().constData());
- #endif
- socketError = socketEngine->error();
- q->setErrorString(socketEngine->errorString());
- return false;
- }
- if (threadData->hasEventDispatcher())
- socketEngine->setReceiver(this);
- #if defined (QABSTRACTSOCKET_DEBUG)
- qDebug("QAbstractSocketPrivate::initSocketLayer(%s, %s) success",
- typeStr.toLatin1().constData(), protocolStr.toLatin1().constData());
- #endif
- return true;
- }
- /*! \internal
- Slot connected to the read socket notifier. This slot is called
- when new data is available for reading, or when the socket has
- been closed. Handles recursive calls.
- */
- bool QAbstractSocketPrivate::canReadNotification()
- {
- Q_Q(QAbstractSocket);
- #if defined (QABSTRACTSOCKET_DEBUG)
- qDebug("QAbstractSocketPrivate::canReadNotification()");
- #endif
- // Prevent recursive calls
- if (readSocketNotifierCalled) {
- if (!readSocketNotifierStateSet) {
- readSocketNotifierStateSet = true;
- readSocketNotifierState = socketEngine->isReadNotificationEnabled();
- socketEngine->setReadNotificationEnabled(false);
- }
- }
- QScopedValueRollback<bool> rsncrollback(readSocketNotifierCalled);
- readSocketNotifierCalled = true;
- if (!isBuffered)
- socketEngine->setReadNotificationEnabled(false);
- // If buffered, read data from the socket into the read buffer
- qint64 newBytes = 0;
- if (isBuffered) {
- // Return if there is no space in the buffer
- if (readBufferMaxSize && buffer.size() >= readBufferMaxSize) {
- #if defined (QABSTRACTSOCKET_DEBUG)
- qDebug("QAbstractSocketPrivate::canReadNotification() buffer is full");
- #endif
- return false;
- }
- // If reading from the socket fails after getting a read
- // notification, close the socket.
- newBytes = buffer.size();
- if (!readFromSocket()) {
- #if defined (QABSTRACTSOCKET_DEBUG)
- qDebug("QAbstractSocketPrivate::canReadNotification() disconnecting socket");
- #endif
- q->disconnectFromHost();
- return false;
- }
- newBytes = buffer.size() - newBytes;
- // If read buffer is full, disable the read socket notifier.
- if (readBufferMaxSize && buffer.size() == readBufferMaxSize) {
- socketEngine->setReadNotificationEnabled(false);
- }
- }
- // only emit readyRead() when not recursing, and only if there is data available
- bool hasData = newBytes > 0
- #ifndef QT_NO_UDPSOCKET
- || (!isBuffered && socketType != QAbstractSocket::TcpSocket && socketEngine && socketEngine->hasPendingDatagrams())
- #endif
- || (!isBuffered && socketType == QAbstractSocket::TcpSocket && socketEngine)
- ;
- if (!emittedReadyRead && hasData) {
- QScopedValueRollback<bool> r(emittedReadyRead);
- emittedReadyRead = true;
- emit q->readyRead();
- }
- // If we were closed as a result of the readyRead() signal,
- // return.
- if (state == QAbstractSocket::UnconnectedState || state == QAbstractSocket::ClosingState) {
- #if defined (QABSTRACTSOCKET_DEBUG)
- qDebug("QAbstractSocketPrivate::canReadNotification() socket is closing - returning");
- #endif
- return true;
- }
- if ((isBuffered || socketType != QAbstractSocket::TcpSocket) && socketEngine)
- socketEngine->setReadNotificationEnabled(readBufferMaxSize == 0 || readBufferMaxSize > q->bytesAvailable());
- // reset the read socket notifier state if we reentered inside the
- // readyRead() connected slot.
- if (readSocketNotifierStateSet && socketEngine &&
- readSocketNotifierState != socketEngine->isReadNotificationEnabled()) {
- socketEngine->setReadNotificationEnabled(readSocketNotifierState);
- readSocketNotifierStateSet = false;
- }
- return true;
- }
- /*! \internal
- Slot connected to the close socket notifier. It's called when the
- socket is closed.
- */
- void QAbstractSocketPrivate::canCloseNotification()
- {
- Q_Q(QAbstractSocket);
- #if defined (QABSTRACTSOCKET_DEBUG)
- qDebug("QAbstractSocketPrivate::canCloseNotification()");
- #endif
- qint64 newBytes = 0;
- if (isBuffered) {
- // Try to read to the buffer, if the read fail we can close the socket.
- newBytes = buffer.size();
- if (!readFromSocket()) {
- q->disconnectFromHost();
- return;
- }
- newBytes = buffer.size() - newBytes;
- if (newBytes) {
- // If there was still some data to be read from the socket
- // then we could get another FD_READ. The disconnect will
- // then occur when we read from the socket again and fail
- // in canReadNotification or by the manually created
- // closeNotification below.
- emit q->readyRead();
- QMetaObject::invokeMethod(socketEngine, "closeNotification", Qt::QueuedConnection);
- }
- } else if (socketType == QAbstractSocket::TcpSocket && socketEngine) {
- emit q->readyRead();
- }
- }
- /*! \internal
- Slot connected to the write socket notifier. It's called during a
- delayed connect or when the socket is ready for writing.
- */
- bool QAbstractSocketPrivate::canWriteNotification()
- {
- #if defined (Q_OS_WIN)
- if (socketEngine && socketEngine->isWriteNotificationEnabled())
- socketEngine->setWriteNotificationEnabled(false);
- #endif
- #if defined (QABSTRACTSOCKET_DEBUG)
- qDebug("QAbstractSocketPrivate::canWriteNotification() flushing");
- #endif
- int tmp = writeBuffer.size();
- flush();
- if (socketEngine) {
- #if defined (Q_OS_WIN)
- if (!writeBuffer.isEmpty())
- socketEngine->setWriteNotificationEnabled(true);
- #else
- if (writeBuffer.isEmpty() && socketEngine->bytesToWrite() == 0)
- socketEngine->setWriteNotificationEnabled(false);
- #endif
- }
- return (writeBuffer.size() < tmp);
- }
- /*! \internal
- Slot connected to a notification of connection status
- change. Either we finished connecting or we failed to connect.
- */
- void QAbstractSocketPrivate::connectionNotification()
- {
- // If in connecting state, check if the connection has been
- // established, otherwise flush pending data.
- if (state == QAbstractSocket::ConnectingState) {
- #if defined (QABSTRACTSOCKET_DEBUG)
- qDebug("QAbstractSocketPrivate::connectionNotification() testing connection");
- #endif
- _q_testConnection();
- }
- }
- /*! \internal
- Writes pending data in the write buffers to the socket. The
- function writes as much as it can without blocking.
- It is usually invoked by canWriteNotification after one or more
- calls to write().
- Emits bytesWritten().
- */
- bool QAbstractSocketPrivate::flush()
- {
- Q_Q(QAbstractSocket);
- if (!socketEngine || !socketEngine->isValid() || (writeBuffer.isEmpty()
- && socketEngine->bytesToWrite() == 0)) {
- #if defined (QABSTRACTSOCKET_DEBUG)
- qDebug("QAbstractSocketPrivate::flush() nothing to do: valid ? %s, writeBuffer.isEmpty() ? %s",
- (socketEngine && socketEngine->isValid()) ? "yes" : "no", writeBuffer.isEmpty() ? "yes" : "no");
- #endif
- // this covers the case when the buffer was empty, but we had to wait for the socket engine to finish
- if (state == QAbstractSocket::ClosingState)
- q->disconnectFromHost();
- return false;
- }
- int nextSize = writeBuffer.nextDataBlockSize();
- const char *ptr = writeBuffer.readPointer();
- // Attempt to write it all in one chunk.
- qint64 written = socketEngine->write(ptr, nextSize);
- if (written < 0) {
- socketError = socketEngine->error();
- q->setErrorString(socketEngine->errorString());
- #if defined (QABSTRACTSOCKET_DEBUG)
- qDebug() << "QAbstractSocketPrivate::flush() write error, aborting." << socketEngine->errorString();
- #endif
- emit q->error(socketError);
- // an unexpected error so close the socket.
- q->abort();
- return false;
- }
- #if defined (QABSTRACTSOCKET_DEBUG)
- qDebug("QAbstractSocketPrivate::flush() %lld bytes written to the network",
- written);
- #endif
- // Remove what we wrote so far.
- writeBuffer.free(written);
- if (written > 0) {
- // Don't emit bytesWritten() recursively.
- if (!emittedBytesWritten) {
- QScopedValueRollback<bool> r(emittedBytesWritten);
- emittedBytesWritten = true;
- emit q->bytesWritten(written);
- }
- }
- if (writeBuffer.isEmpty() && socketEngine && socketEngine->isWriteNotificationEnabled()
- && !socketEngine->bytesToWrite())
- socketEngine->setWriteNotificationEnabled(false);
- if (state == QAbstractSocket::ClosingState)
- q->disconnectFromHost();
- return true;
- }
- #ifndef QT_NO_NETWORKPROXY
- /*! \internal
- Resolve the proxy to its final value.
- */
- void QAbstractSocketPrivate::resolveProxy(const QString &hostname, quint16 port)
- {
- QList<QNetworkProxy> proxies;
- if (proxy.type() != QNetworkProxy::DefaultProxy) {
- // a non-default proxy was set with setProxy
- proxies << proxy;
- } else {
- // try the application settings instead
- QNetworkProxyQuery query(hostname, port, QString(),
- socketType == QAbstractSocket::TcpSocket ?
- QNetworkProxyQuery::TcpSocket :
- QNetworkProxyQuery::UdpSocket);
- proxies = QNetworkProxyFactory::proxyForQuery(query);
- }
- // return the first that we can use
- foreach (const QNetworkProxy &p, proxies) {
- if (socketType == QAbstractSocket::UdpSocket &&
- (p.capabilities() & QNetworkProxy::UdpTunnelingCapability) == 0)
- continue;
- if (socketType == QAbstractSocket::TcpSocket &&
- (p.capabilities() & QNetworkProxy::TunnelingCapability) == 0)
- continue;
- proxyInUse = p;
- return;
- }
- // no proxy found
- // DefaultProxy here will raise an error
- proxyInUse = QNetworkProxy();
- }
- /*!
- \internal
- Starts the connection to \a host, like _q_startConnecting below,
- but without hostname resolution.
- */
- void QAbstractSocketPrivate::startConnectingByName(const QString &host)
- {
- Q_Q(QAbstractSocket);
- if (state == QAbstractSocket::ConnectingState || state == QAbstractSocket::ConnectedState)
- return;
- #if defined(QABSTRACTSOCKET_DEBUG)
- qDebug("QAbstractSocketPrivate::startConnectingByName(host == %s)", qPrintable(host));
- #endif
- // ### Let the socket engine drive this?
- state = QAbstractSocket::ConnectingState;
- emit q->stateChanged(state);
- connectTimeElapsed = 0;
- if (initSocketLayer(QAbstractSocket::UnknownNetworkLayerProtocol)) {
- if (socketEngine->connectToHostByName(host, port) ||
- socketEngine->state() == QAbstractSocket::ConnectingState) {
- cachedSocketDescriptor = socketEngine->socketDescriptor();
- return;
- }
- // failed to connect
- socketError = socketEngine->error();
- q->setErrorString(socketEngine->errorString());
- }
- state = QAbstractSocket::UnconnectedState;
- emit q->error(socketError);
- emit q->stateChanged(state);
- }
- #endif
- /*! \internal
- Slot connected to QHostInfo::lookupHost() in connectToHost(). This
- function starts the process of connecting to any number of
- candidate IP addresses for the host, if it was found. Calls
- _q_connectToNextAddress().
- */
- void QAbstractSocketPrivate::_q_startConnecting(const QHostInfo &hostInfo)
- {
- Q_Q(QAbstractSocket);
- addresses.clear();
- if (state != QAbstractSocket::HostLookupState)
- return;
- if (hostLookupId != -1 && hostLookupId != hostInfo.lookupId()) {
- qWarning("QAbstractSocketPrivate::_q_startConnecting() received hostInfo for wrong lookup ID %d expected %d", hostInfo.lookupId(), hostLookupId);
- }
- // Only add the addresses for the preferred network layer.
- // Or all if preferred network layer is not set.
- if (preferredNetworkLayerProtocol == QAbstractSocket::UnknownNetworkLayerProtocol || preferredNetworkLayerProtocol == QAbstractSocket::AnyIPProtocol) {
- addresses = hostInfo.addresses();
- } else {
- foreach (const QHostAddress &address, hostInfo.addresses())
- if (address.protocol() == preferredNetworkLayerProtocol)
- addresses += address;
- }
- #if defined(QABSTRACTSOCKET_DEBUG)
- QString s = QLatin1String("{");
- for (int i = 0; i < addresses.count(); ++i) {
- if (i != 0) s += QLatin1String(", ");
- s += addresses.at(i).toString();
- }
- s += QLatin1Char('}');
- qDebug("QAbstractSocketPrivate::_q_startConnecting(hostInfo == %s)", s.toLatin1().constData());
- #endif
- // Try all addresses twice.
- addresses += addresses;
- // If there are no addresses in the host list, report this to the
- // user.
- if (addresses.isEmpty()) {
- #if defined(QABSTRACTSOCKET_DEBUG)
- qDebug("QAbstractSocketPrivate::_q_startConnecting(), host not found");
- #endif
- state = QAbstractSocket::UnconnectedState;
- socketError = QAbstractSocket::HostNotFoundError;
- q->setErrorString(QAbstractSocket::tr("Host not found"));
- emit q->stateChanged(state);
- emit q->error(QAbstractSocket::HostNotFoundError);
- return;
- }
- // Enter Connecting state (see also sn_write, which is called by
- // the write socket notifier after connect())
- state = QAbstractSocket::ConnectingState;
- emit q->stateChanged(state);
- // Report the successful host lookup
- emit q->hostFound();
- // Reset the total time spent connecting.
- connectTimeElapsed = 0;
- // The addresses returned by the lookup will be tested one after
- // another by _q_connectToNextAddress().
- _q_connectToNextAddress();
- }
- /*! \internal
- Called by a queued or direct connection from _q_startConnecting() or
- _q_testConnection(), this function takes the first address of the
- pending addresses list and tries to connect to it. If the
- connection succeeds, QAbstractSocket will emit
- connected(). Otherwise, error(ConnectionRefusedError) or
- error(SocketTimeoutError) is emitted.
- */
- void QAbstractSocketPrivate::_q_connectToNextAddress()
- {
- Q_Q(QAbstractSocket);
- do {
- // Check for more pending addresses
- if (addresses.isEmpty()) {
- #if defined(QABSTRACTSOCKET_DEBUG)
- qDebug("QAbstractSocketPrivate::_q_connectToNextAddress(), all addresses failed.");
- #endif
- state = QAbstractSocket::UnconnectedState;
- if (socketEngine) {
- if ((socketEngine->error() == QAbstractSocket::UnknownSocketError
- #ifdef Q_OS_AIX
- // On AIX, the second connect call will result in EINVAL and not
- // ECONNECTIONREFUSED; although the meaning is the same.
- || socketEngine->error() == QAbstractSocket::UnsupportedSocketOperationError
- #endif
- ) && 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(QABSTRACTSOCKET_DEBUG)
- qDebug("QAbstractSocketPrivate::_q_connectToNextAddress(), connecting to %s:%i, %d left to try",
- host.toString().toLatin1().constData(), port, addresses.count());
- #endif
- if (!initSocketLayer(host.protocol())) {
- // hope that the next address is better
- #if defined(QABSTRACTSOCKET_DEBUG)
- qDebug("QAbstractSocketPrivate::_q_connectToNextAddress(), failed to initialize sock layer");
- #endif
- 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) {
- #if defined(QABSTRACTSOCKET_DEBUG)
- qDebug("QAbstractSocketPrivate::_q_connectToNextAddress(), connection failed (%s)",
- socketEngine->errorString().toLatin1().constData());
- #endif
- continue;
- }
- // Start the connect timer.
- if (threadData->hasEventDispatcher()) {
- 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);
- }
- /*! \internal
- Tests if a connection has been established. If it has, connected()
- is emitted. Otherwise, _q_connectToNextAddress() is invoked.
- */
- void QAbstractSocketPrivate::_q_testConnection()
- {
- if (socketEngine) {
- if (threadData->hasEventDispatcher()) {
- if (connectTimer)
- connectTimer->stop();
- }
- if (socketEngine->state() == QAbstractSocket::ConnectedState) {
- // Fetch the parameters if our connection is completed;
- // otherwise, fall out and try the next address.
- fetchConnectionParameters();
- if (pendingClose) {
- q_func()->disconnectFromHost();
- pendingClose = false;
- }
- return;
- }
- // don't retry the other addresses if we had a proxy error
- if (isProxyError(socketEngine->error()))
- addresses.clear();
- }
- if (threadData->hasEventDispatcher()) {
- if (connectTimer)
- connectTimer->stop();
- }
- #if defined(QABSTRACTSOCKET_DEBUG)
- qDebug("QAbstractSocketPrivate::_q_testConnection() connection failed,"
- " checking for alternative addresses");
- #endif
- _q_connectToNextAddress();
- }
- /*! \internal
- This function is called after a certain number of seconds has
- passed while waiting for a connection. It simply tests the
- connection, and continues to the next address if the connection
- failed.
- */
- void QAbstractSocketPrivate::_q_abortConnectionAttempt()
- {
- Q_Q(QAbstractSocket);
- #if defined(QABSTRACTSOCKET_DEBUG)
- qDebug("QAbstractSocketPrivate::_q_abortConnectionAttempt() (timed out)");
- #endif
- if (socketEngine)
- socketEngine->setWriteNotificationEnabled(false);
- connectTimer->stop();
- if (addresses.isEmpty()) {
- state = QAbstractSocket::UnconnectedState;
- socketError = QAbstractSocket::SocketTimeoutError;
- q->setErrorString(QAbstractSocket::tr("Connection timed out"));
- emit q->stateChanged(state);
- emit q->error(socketError);
- } else {
- _q_connectToNextAddress();
- }
- }
- void QAbstractSocketPrivate::_q_forceDisconnect()
- {
- Q_Q(QAbstractSocket);
- if (socketEngine && socketEngine->isValid() && state == QAbstractSocket::ClosingState) {
- socketEngine->close();
- q->disconnectFromHost();
- }
- }
- /*! \internal
- Reads data from the socket layer into the read buffer. Returns
- true on success; otherwise false.
- */
- bool QAbstractSocketPrivate::readFromSocket()
- {
- Q_Q(QAbstractSocket);
- // Find how many bytes we can read from the socket layer.
- qint64 bytesToRead = socketEngine->bytesAvailable();
- if (bytesToRead == 0) {
- // Under heavy load, certain conditions can trigger read notifications
- // for socket notifiers on which there is no activity. If we continue
- // to read 0 bytes from the socket, we will trigger behavior similar
- // to that which signals a remote close. When we hit this condition,
- // we try to read 4k of data from the socket, which will give us either
- // an EAGAIN/EWOULDBLOCK if the connection is alive (i.e., the remote
- // host has _not_ disappeared).
- bytesToRead = 4096;
- }
- if (readBufferMaxSize && bytesToRead > (readBufferMaxSize - buffer.size()))
- bytesToRead = readBufferMaxSize - buffer.size();
- #if defined(QABSTRACTSOCKET_DEBUG)
- qDebug("QAbstractSocketPrivate::readFromSocket() about to read %d bytes",
- int(bytesToRead));
- #endif
- // Read from the socket, store data in the read buffer.
- char *ptr = buffer.reserve(bytesToRead);
- qint64 readBytes = socketEngine->read(ptr, bytesToRead);
- if (readBytes == -2) {
- // No bytes currently available for reading.
- buffer.chop(bytesToRead);
- return true;
- }
- buffer.chop(int(bytesToRead - (readBytes < 0 ? qint64(0) : readBytes)));
- #if defined(QABSTRACTSOCKET_DEBUG)
- qDebug("QAbstractSocketPrivate::readFromSocket() got %d bytes, buffer size = %d",
- int(readBytes), buffer.size());
- #endif
- if (!socketEngine->isValid()) {
- socketError = socketEngine->error();
- q->setErrorString(socketEngine->errorString());
- emit q->error(socketError);
- #if defined(QABSTRACTSOCKET_DEBUG)
- qDebug("QAbstractSocketPrivate::readFromSocket() read failed: %s",
- q->errorString().toLatin1().constData());
- #endif
- resetSocketLayer();
- return false;
- }
- return true;
- }
- /*! \internal
- Sets up the internal state after the connection has succeeded.
- */
- void QAbstractSocketPrivate::fetchConnectionParameters()
- {
- Q_Q(QAbstractSocket);
- peerName = hostName;
- if (socketEngine) {
- socketEngine->setReadNotificationEnabled(true);
- socketEngine->setWriteNotificationEnabled(true);
- localPort = socketEngine->localPort();
- peerPort = socketEngine->peerPort();
- localAddress = socketEngine->localAddress();
- peerAddress = socketEngine->peerAddress();
- cachedSocketDescriptor = socketEngine->socketDescriptor();
- }
- state = QAbstractSocket::ConnectedState;
- emit q->stateChanged(state);
- emit q->connected();
- #if defined(QABSTRACTSOCKET_DEBUG)
- qDebug("QAbstractSocketPrivate::fetchConnectionParameters() connection to %s:%i established",
- host.toString().toLatin1().constData(), port);
- #endif
- }
- void QAbstractSocketPrivate::pauseSocketNotifiers(QAbstractSocket *socket)
- {
- QAbstractSocketEngine *socketEngine = socket->d_func()->socketEngine;
- if (!socketEngine)
- return;
- socket->d_func()->prePauseReadSocketNotifierState = socketEngine->isReadNotificationEnabled();
- socket->d_func()->prePauseWriteSocketNotifierState = socketEngine->isWriteNotificationEnabled();
- socket->d_func()->prePauseExceptionSocketNotifierState = socketEngine->isExceptionNotificationEnabled();
- socketEngine->setReadNotificationEnabled(false);
- socketEngine->setWriteNotificationEnabled(false);
- socketEngine->setExceptionNotificationEnabled(false);
- }
- void QAbstractSocketPrivate::resumeSocketNotifiers(QAbstractSocket *socket)
- {
- QAbstractSocketEngine *socketEngine = socket->d_func()->socketEngine;
- if (!socketEngine)
- return;
- socketEngine->setReadNotificationEnabled(socket->d_func()->prePauseReadSocketNotifierState);
- socketEngine->setWriteNotificationEnabled(socket->d_func()->prePauseWriteSocketNotifierState);
- socketEngine->setExceptionNotificationEnabled(socket->d_func()->prePauseExceptionSocketNotifierState);
- }
- QAbstractSocketEngine* QAbstractSocketPrivate::getSocketEngine(QAbstractSocket *socket)
- {
- return socket->d_func()->socketEngine;
- }
- /*! \internal
- Constructs a new abstract socket of type \a socketType. The \a
- parent …
Large files files are truncated, but you can click here to view the full file