diff options
Diffstat (limited to 'src/network/socket/qnativesocketengine.cpp')
-rw-r--r-- | src/network/socket/qnativesocketengine.cpp | 1140 |
1 files changed, 1140 insertions, 0 deletions
diff --git a/src/network/socket/qnativesocketengine.cpp b/src/network/socket/qnativesocketengine.cpp new file mode 100644 index 0000000..b25b887 --- /dev/null +++ b/src/network/socket/qnativesocketengine.cpp @@ -0,0 +1,1140 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//#define QNATIVESOCKETENGINE_DEBUG + +/*! \class QNativeSocketEngine + \internal + + \brief The QNativeSocketEngine class provides low level access to a socket. + + \reentrant + \ingroup io + \inmodule QtNetwork + + QtSocketLayer provides basic socket functionality provided by the + operating system. It also keeps track of what state the socket is + in, and which errors that occur. + + The classes QTcpSocket, QUdpSocket and QTcpServer provide a + higher level API, and are in general more useful for the common + application. + + There are two main ways of initializing the a QNativeSocketEngine; either + create a new socket by passing the socket type (TcpSocket or + UdpSocket) and network layer protocol (IPv4Protocol or + IPv6Protocol) to initialize(), or pass an existing socket + descriptor and have QNativeSocketEngine determine the type and protocol + itself. The native socket descriptor can later be fetched by + calling socketDescriptor(). The socket is made non-blocking, but + blocking behavior can still be achieved by calling waitForRead() + and waitForWrite(). isValid() can be called to check if the socket + has been successfully initialized and is ready to use. + + To connect to a host, determine its address and pass this and the + port number to connectToHost(). The socket can then be used as a + TCP or UDP client. Otherwise; bind(), listen() and accept() are + used to have the socket function as a TCP or UDP server. Call + close() to close the socket. + + bytesAvailable() is called to determine how much data is available + for reading. read() and write() are used by both TCP and UDP + clients to exchange data with the connected peer. UDP clients can + also call hasMoreDatagrams(), nextDatagramSize(), + readDatagram(), and writeDatagram(). + + Call state() to determine the state of the socket, for + example, ListeningState or ConnectedState. socketType() tells + whether the socket is a TCP socket or a UDP socket, or if the + socket type is unknown. protocol() is used to determine the + socket's network layer protocol. + + localAddress(), localPort() are called to find the address and + port that are currently bound to the socket. If the socket is + connected, peerAddress() and peerPort() determine the address and + port of the connected peer. + + Finally, if any function should fail, error() and + errorString() can be called to determine the cause of the error. +*/ + +#include <qabstracteventdispatcher.h> +#include <qsocketnotifier.h> + +#include "qnativesocketengine_p.h" +#include <private/qthread_p.h> +#include <private/qobject_p.h> + +#if !defined(QT_NO_NETWORKPROXY) +# include "qnetworkproxy.h" +# include "qabstractsocket.h" +# include "qtcpserver.h" +#endif + +QT_BEGIN_NAMESPACE + +//#define QNATIVESOCKETENGINE_DEBUG + +#define Q_VOID + +// Common constructs +#define Q_CHECK_VALID_SOCKETLAYER(function, returnValue) do { \ + if (!isValid()) { \ + qWarning(""#function" was called on an uninitialized socket device"); \ + return returnValue; \ + } } while (0) +#define Q_CHECK_INVALID_SOCKETLAYER(function, returnValue) do { \ + if (isValid()) { \ + qWarning(""#function" was called on an already initialized socket device"); \ + return returnValue; \ + } } while (0) +#define Q_CHECK_STATE(function, checkState, returnValue) do { \ + if (d->socketState != (checkState)) { \ + qWarning(""#function" was not called in "#checkState); \ + return (returnValue); \ + } } while (0) +#define Q_CHECK_NOT_STATE(function, checkState, returnValue) do { \ + if (d->socketState == (checkState)) { \ + qWarning(""#function" was called in "#checkState); \ + return (returnValue); \ + } } while (0) +#define Q_CHECK_STATES(function, state1, state2, returnValue) do { \ + if (d->socketState != (state1) && d->socketState != (state2)) { \ + qWarning(""#function" was called" \ + " not in "#state1" or "#state2); \ + return (returnValue); \ + } } while (0) +#define Q_CHECK_TYPE(function, type, returnValue) do { \ + if (d->socketType != (type)) { \ + qWarning(#function" was called by a" \ + " socket other than "#type""); \ + return (returnValue); \ + } } while (0) +#define Q_TR(a) QT_TRANSLATE_NOOP(QNativeSocketEngine, a) + +/*! \internal + Constructs the private class and initializes all data members. + + On Windows, WSAStartup is called "recursively" for every + concurrent QNativeSocketEngine. This is safe, because WSAStartup and + WSACleanup are reference counted. +*/ +QNativeSocketEnginePrivate::QNativeSocketEnginePrivate() +{ + socketDescriptor = -1; + readNotifier = 0; + writeNotifier = 0; + exceptNotifier = 0; +} + +/*! \internal + Destructs the private class. +*/ +QNativeSocketEnginePrivate::~QNativeSocketEnginePrivate() +{ +} + +/*! \internal + + Sets the error and error string if not set already. The only + interesting error is the first one that occurred, and not the last + one. +*/ +void QNativeSocketEnginePrivate::setError(QAbstractSocket::SocketError error, ErrorString errorString) const +{ + if (hasSetSocketError) { + // Only set socket errors once for one engine; expect the + // socket to recreate its engine after an error. Note: There's + // one exception: SocketError(11) bypasses this as it's purely + // a temporary internal error condition. + return; + } + if (error != QAbstractSocket::SocketError(11)) + hasSetSocketError = true; + + socketError = error; + + switch (errorString) { + case NonBlockingInitFailedErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Unable to initialize non-blocking socket")); + break; + case BroadcastingInitFailedErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Unable to initialize broadcast socket")); + break; + case NoIpV6ErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Attempt to use IPv6 socket on a platform with no IPv6 support")); + break; + case RemoteHostClosedErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "The remote host closed the connection")); + break; + case TimeOutErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Network operation timed out")); + break; + case ResourceErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Out of resources")); + break; + case OperationUnsupportedErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Unsupported socket operation")); + break; + case ProtocolUnsupportedErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Protocol type not supported")); + break; + case InvalidSocketErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Invalid socket descriptor")); + break; + case HostUnreachableErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Host unreachable")); + break; + case NetworkUnreachableErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Network unreachable")); + break; + case AccessErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Permission denied")); + break; + case ConnectionTimeOutErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Connection timed out")); + break; + case ConnectionRefusedErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Connection refused")); + break; + case AddressInuseErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "The bound address is already in use")); + break; + case AddressNotAvailableErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "The address is not available")); + break; + case AddressProtectedErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "The address is protected")); + break; + case DatagramTooLargeErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Datagram was too large to send")); + break; + case SendDatagramErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Unable to send a message")); + break; + case ReceiveDatagramErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Unable to receive a message")); + break; + case WriteErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Unable to write")); + break; + case ReadErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Network error")); + break; + case PortInuseErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Another socket is already listening on the same port")); + break; + case NotSocketErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Operation on non-socket")); + break; + case InvalidProxyTypeString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "The proxy type is invalid for this operation")); + break; + case UnknownSocketErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Unknown error")); + break; + } +} + +bool QNativeSocketEnginePrivate::checkProxy(const QHostAddress &address) +{ + if (address == QHostAddress::LocalHost || address == QHostAddress::LocalHostIPv6) + return true; + +#if !defined(QT_NO_NETWORKPROXY) + QObject *parent = q_func()->parent(); + QNetworkProxy proxy; + if (QAbstractSocket *socket = qobject_cast<QAbstractSocket *>(parent)) { + proxy = socket->proxy(); + } else if (QTcpServer *server = qobject_cast<QTcpServer *>(parent)) { + proxy = server->proxy(); + } else { + // no parent -> no proxy + return true; + } + + if (proxy.type() == QNetworkProxy::DefaultProxy) + proxy = QNetworkProxy::applicationProxy(); + + if (proxy.type() != QNetworkProxy::DefaultProxy && + proxy.type() != QNetworkProxy::NoProxy) { + // QNativeSocketEngine doesn't do proxies + setError(QAbstractSocket::UnsupportedSocketOperationError, + QNativeSocketEnginePrivate::InvalidProxyTypeString); + return false; + } +#endif + + return true; +} + +/*! + Constructs a QNativeSocketEngine. + + \sa initialize() +*/ +QNativeSocketEngine::QNativeSocketEngine(QObject *parent) + : QAbstractSocketEngine(*new QNativeSocketEnginePrivate(), parent) +{ +} + +/*! + Destructs a QNativeSocketEngine. +*/ +QNativeSocketEngine::~QNativeSocketEngine() +{ + close(); +} + +/*! + Initializes a QNativeSocketEngine by creating a new socket of type \a + socketType and network layer protocol \a protocol. Returns true on + success; otherwise returns false. + + If the socket was already initialized, this function closes the + socket before reeinitializing it. + + The new socket is non-blocking, and for UDP sockets it's also + broadcast enabled. +*/ +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)) { +#if defined (QNATIVESOCKETENGINE_DEBUG) + QString typeStr = QLatin1String("UnknownSocketType"); + if (socketType == QAbstractSocket::TcpSocket) typeStr = QLatin1String("TcpSocket"); + else if (socketType == QAbstractSocket::UdpSocket) typeStr = QLatin1String("UdpSocket"); + QString protocolStr = QLatin1String("UnknownProtocol"); + if (protocol == QAbstractSocket::IPv4Protocol) protocolStr = QLatin1String("IPv4Protocol"); + else if (protocol == QAbstractSocket::IPv6Protocol) protocolStr = QLatin1String("IPv6Protocol"); + qDebug("QNativeSocketEngine::initialize(type == %s, protocol == %s) failed: %s", + typeStr.toLatin1().constData(), protocolStr.toLatin1().constData(), d->socketErrorString.toLatin1().constData()); +#endif + 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; +} + +/*! \overload + + Initializes the socket using \a socketDescriptor instead of + creating a new one. The socket type and network layer protocol are + determined automatically. The socket's state is set to \a + socketState. + + If the socket type is either TCP or UDP, it is made non-blocking. + UDP sockets are also broadcast enabled. + */ +bool QNativeSocketEngine::initialize(int socketDescriptor, QAbstractSocket::SocketState socketState) +{ + Q_D(QNativeSocketEngine); + + if (isValid()) + close(); + + d->socketDescriptor = socketDescriptor; + + // determine socket type and protocol + if (!d->fetchConnectionParameters()) { +#if defined (QNATIVESOCKETENGINE_DEBUG) + qDebug("QNativeSocketEngine::initialize(socketDescriptor == %i) failed: %s", + socketDescriptor, d->socketErrorString.toLatin1().constData()); +#endif + d->socketDescriptor = -1; + return false; + } + + if (d->socketType != QAbstractSocket::UnknownSocketType) { + // 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 (d->socketType == QAbstractSocket::UdpSocket + && !setOption(BroadcastSocketOption, 1)) { + d->setError(QAbstractSocket::UnsupportedSocketOperationError, + QNativeSocketEnginePrivate::BroadcastingInitFailedErrorString); + close(); + return false; + } + } + + d->socketState = socketState; + return true; +} + +/*! + Returns true if the socket is valid; otherwise returns false. A + socket is valid if it has not been successfully initialized, or if + it has been closed. +*/ +bool QNativeSocketEngine::isValid() const +{ + Q_D(const QNativeSocketEngine); + return d->socketDescriptor != -1; +} + +/*! + Returns the native socket descriptor. Any use of this descriptor + stands the risk of being non-portable. +*/ +int QNativeSocketEngine::socketDescriptor() const +{ + Q_D(const QNativeSocketEngine); + return d->socketDescriptor; +} + +/*! + Connects to the IP address and port specified by \a address and \a + port. If the connection is established, this function returns true + and the socket enters ConnectedState. Otherwise, false is + returned. + + If false is returned, state() should be called to see if the + socket is in ConnectingState. If so, a delayed TCP connection is + taking place, and connectToHost() must be called again later to + determine if the connection was established successfully or + not. The second connection attempt must be made when the socket is + ready for writing. This state can be determined either by + connecting a QSocketNotifier to the socket descriptor returned by + socketDescriptor(), or by calling the blocking function + waitForWrite(). + + Example: + \snippet doc/src/snippets/code/src_network_socket_qnativesocketengine.cpp 0 + + Otherwise, error() should be called to determine the cause of the + error. +*/ +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; +} + +/*! + If there's a connection activity on the socket, process it. Then + notify our parent if there really was activity. +*/ +void QNativeSocketEngine::connectionNotification() +{ + Q_D(QNativeSocketEngine); + Q_ASSERT(state() == QAbstractSocket::ConnectingState); + + connectToHost(d->peerAddress, d->peerPort); + if (state() != QAbstractSocket::ConnectingState) { + // we changed states + QAbstractSocketEngine::connectionNotification(); + } +} + +/*! + Connects to the remote host name given by \a name on port \a + port. When this function is called, the upper-level will not + perform a hostname lookup. + + The native socket engine does not support this operation, + but some other socket engines (notably proxy-based ones) do. +*/ +bool QNativeSocketEngine::connectToHostByName(const QString &name, quint16 port) +{ + Q_UNUSED(name); + Q_UNUSED(port); + Q_D(QNativeSocketEngine); + d->setError(QAbstractSocket::UnsupportedSocketOperationError, + QNativeSocketEnginePrivate::OperationUnsupportedErrorString); + return false; +} + +/*! + Binds the socket to the address \a address and port \a + port. Returns true on success; otherwise false is returned. The + port may be 0, in which case an arbitrary unused port is assigned + automatically by the operating system. + + Servers call this function to set up the server's address and + port. TCP servers must in addition call listen() after bind(). +*/ +bool QNativeSocketEngine::bind(const QHostAddress &address, quint16 port) +{ + Q_D(QNativeSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::bind(), 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_STATE(QNativeSocketEngine::bind(), QAbstractSocket::UnconnectedState, false); + + if (!d->nativeBind(address, port)) + return false; + + d->fetchConnectionParameters(); + return true; +} + +/*! + Prepares a TCP server for accepting incoming connections. This + function must be called after bind(), and only by TCP sockets. + + After this function has been called, pending client connections + are detected by checking if the socket is ready for reading. This + can be done by either creating a QSocketNotifier, passing the + socket descriptor returned by socketDescriptor(), or by calling + the blocking function waitForRead(). + + Example: + \snippet doc/src/snippets/code/src_network_socket_qnativesocketengine.cpp 1 + + \sa bind(), accept() +*/ +bool QNativeSocketEngine::listen() +{ + Q_D(QNativeSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::listen(), false); + Q_CHECK_STATE(QNativeSocketEngine::listen(), QAbstractSocket::BoundState, false); + Q_CHECK_TYPE(QNativeSocketEngine::listen(), QAbstractSocket::TcpSocket, false); + + // We're using a backlog of 50. Most modern kernels support TCP + // syncookies by default, and if they do, the backlog is ignored. + // When there is no support for TCP syncookies, this value is + // fine. + return d->nativeListen(50); +} + +/*! + Accepts a pending connection from the socket, which must be in + ListeningState, and returns its socket descriptor. If no pending + connections are available, -1 is returned. + + \sa bind(), listen() +*/ +int QNativeSocketEngine::accept() +{ + Q_D(QNativeSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::accept(), -1); + Q_CHECK_STATE(QNativeSocketEngine::accept(), QAbstractSocket::ListeningState, false); + Q_CHECK_TYPE(QNativeSocketEngine::accept(), QAbstractSocket::TcpSocket, false); + + return d->nativeAccept(); +} + +/*! + Returns the number of bytes that are currently available for + reading. On error, -1 is returned. + + For UDP sockets, this function returns the accumulated size of all + pending datagrams, and it is therefore more useful for UDP sockets + to call hasPendingDatagrams() and pendingDatagramSize(). +*/ +qint64 QNativeSocketEngine::bytesAvailable() const +{ + Q_D(const QNativeSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::bytesAvailable(), -1); + Q_CHECK_NOT_STATE(QNativeSocketEngine::bytesAvailable(), QAbstractSocket::UnconnectedState, false); + + return d->nativeBytesAvailable(); +} + +/*! + Returns true if there is at least one datagram pending. This + function is only called by UDP sockets, where a datagram can have + a size of 0. TCP sockets call bytesAvailable(). +*/ +bool QNativeSocketEngine::hasPendingDatagrams() const +{ + Q_D(const QNativeSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::hasPendingDatagrams(), false); + Q_CHECK_NOT_STATE(QNativeSocketEngine::hasPendingDatagrams(), QAbstractSocket::UnconnectedState, false); + Q_CHECK_TYPE(QNativeSocketEngine::hasPendingDatagrams(), QAbstractSocket::UdpSocket, false); + + return d->nativeHasPendingDatagrams(); +} + +/*! + Returns the size of the pending datagram, or -1 if no datagram is + pending. A datagram size of 0 is perfectly valid. This function is + called by UDP sockets before receiveMessage(). For TCP sockets, + call bytesAvailable(). +*/ +qint64 QNativeSocketEngine::pendingDatagramSize() const +{ + Q_D(const QNativeSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::pendingDatagramSize(), -1); + Q_CHECK_TYPE(QNativeSocketEngine::pendingDatagramSize(), QAbstractSocket::UdpSocket, false); + + return d->nativePendingDatagramSize(); +} + +/*! + Reads up to \a maxSize bytes of a datagram from the socket, + stores it in \a data and returns the number of bytes read. The + address and port of the sender are stored in \a address and \a + port. If either of these pointers is 0, the corresponding value is + discarded. + + To avoid unnecessarily loss of data, call pendingDatagramSize() to + determine the size of the pending message before reading it. If \a + maxSize is too small, the rest of the datagram will be lost. + + Returns -1 if an error occurred. + + \sa hasPendingDatagrams() +*/ +qint64 QNativeSocketEngine::readDatagram(char *data, qint64 maxSize, QHostAddress *address, + quint16 *port) +{ + Q_D(QNativeSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::readDatagram(), -1); + Q_CHECK_TYPE(QNativeSocketEngine::readDatagram(), QAbstractSocket::UdpSocket, false); + + return d->nativeReceiveDatagram(data, maxSize, address, port); +} + +/*! + Writes a UDP datagram of size \a size bytes to the socket from + \a data to the address \a host on port \a port, and returns the + number of bytes written, or -1 if an error occurred. + + Only one datagram is sent, and if there is too much data to fit + into a single datagram, the operation will fail and error() + will return QAbstractSocket::DatagramTooLargeError. Operating systems impose an + upper limit to the size of a datagram, but this size is different + on almost all platforms. Sending large datagrams is in general + disadvised, as even if they are sent successfully, they are likely + to be fragmented before arriving at their destination. + + Experience has shown that it is in general safe to send datagrams + no larger than 512 bytes. + + \sa readDatagram() +*/ +qint64 QNativeSocketEngine::writeDatagram(const char *data, qint64 size, + const QHostAddress &host, quint16 port) +{ + Q_D(QNativeSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::writeDatagram(), -1); + Q_CHECK_TYPE(QNativeSocketEngine::writeDatagram(), QAbstractSocket::UdpSocket, -1); + return d->nativeSendDatagram(data, size, host, port); +} + +/*! + Writes a block of \a size bytes from \a data to the socket. + Returns the number of bytes written, or -1 if an error occurred. +*/ +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); +} + +/*! + Reads up to \a maxSize bytes into \a data from the socket. + Returns the number of bytes read, or -1 if an error occurred. +*/ +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; +} + +/*! + Closes the socket. In order to use the socket again, initialize() + must be called. +*/ +void QNativeSocketEngine::close() +{ + Q_D(QNativeSocketEngine); + if (d->readNotifier) + d->readNotifier->setEnabled(false); + if (d->writeNotifier) + d->writeNotifier->setEnabled(false); + if (d->exceptNotifier) + d->exceptNotifier->setEnabled(false); + + if(d->socketDescriptor != -1) { + d->nativeClose(); + d->socketDescriptor = -1; + } + d->socketState = QAbstractSocket::UnconnectedState; + d->hasSetSocketError = false; + d->localPort = 0; + d->localAddress.clear(); + d->peerPort = 0; + d->peerAddress.clear(); + if (d->readNotifier) { + qDeleteInEventHandler(d->readNotifier); + d->readNotifier = 0; + } + if (d->writeNotifier) { + qDeleteInEventHandler(d->writeNotifier); + d->writeNotifier = 0; + } + if (d->exceptNotifier) { + qDeleteInEventHandler(d->exceptNotifier); + d->exceptNotifier = 0; + } +} + +/*! + Waits for \a msecs milliseconds or until the socket is ready for + reading. If \a timedOut is not 0 and \a msecs milliseconds have + passed, the value of \a timedOut is set to true. + + Returns true if data is available for reading; otherwise returns + false. + + This is a blocking function call; its use is disadvised in a + single threaded application, as the whole thread will stop + responding until the function returns. waitForRead() is most + useful when there is no event loop available. The general approach + is to create a QSocketNotifier, passing the socket descriptor + returned by socketDescriptor() to its constructor. +*/ +bool QNativeSocketEngine::waitForRead(int msecs, bool *timedOut) +{ + Q_D(const QNativeSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::waitForRead(), false); + Q_CHECK_NOT_STATE(QNativeSocketEngine::waitForRead(), + QAbstractSocket::UnconnectedState, false); + + if (timedOut) + *timedOut = false; + + int ret = d->nativeSelect(msecs, true); + if (ret == 0) { + if (timedOut) + *timedOut = true; + d->setError(QAbstractSocket::SocketTimeoutError, + QNativeSocketEnginePrivate::TimeOutErrorString); + return false; + } else if (state() == QAbstractSocket::ConnectingState) { + connectToHost(d->peerAddress, d->peerPort); + } + + return ret > 0; +} + +/*! + Waits for \a msecs milliseconds or until the socket is ready for + writing. If \a timedOut is not 0 and \a msecs milliseconds have + passed, the value of \a timedOut is set to true. + + Returns true if data is available for writing; otherwise returns + false. + + This is a blocking function call; its use is disadvised in a + single threaded application, as the whole thread will stop + responding until the function returns. waitForWrite() is most + useful when there is no event loop available. The general approach + is to create a QSocketNotifier, passing the socket descriptor + returned by socketDescriptor() to its constructor. +*/ +bool QNativeSocketEngine::waitForWrite(int msecs, bool *timedOut) +{ + Q_D(const QNativeSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::waitForWrite(), false); + Q_CHECK_NOT_STATE(QNativeSocketEngine::waitForWrite(), + QAbstractSocket::UnconnectedState, false); + + if (timedOut) + *timedOut = false; + + int ret = d->nativeSelect(msecs, false); + // On Windows, the socket is in connected state if a call to + // select(writable) is successful. In this case we should not + // issue a second call to WSAConnect() +#if defined (Q_WS_WIN) + if (ret > 0) { + setState(QAbstractSocket::ConnectedState); + d_func()->fetchConnectionParameters(); + return true; + } +#endif + + if (ret == 0) { + if (timedOut) + *timedOut = true; + d->setError(QAbstractSocket::SocketTimeoutError, + QNativeSocketEnginePrivate::TimeOutErrorString); + return false; + } else if (state() == QAbstractSocket::ConnectingState) { + connectToHost(d->peerAddress, d->peerPort); + } + + return ret > 0; +} + +bool QNativeSocketEngine::waitForReadOrWrite(bool *readyToRead, bool *readyToWrite, + bool checkRead, bool checkWrite, + int msecs, bool *timedOut) +{ + Q_D(const QNativeSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::waitForWrite(), false); + Q_CHECK_NOT_STATE(QNativeSocketEngine::waitForReadOrWrite(), + QAbstractSocket::UnconnectedState, false); + + int ret = d->nativeSelect(msecs, checkRead, checkWrite, readyToRead, readyToWrite); + // On Windows, the socket is in connected state if a call to + // select(writable) is successful. In this case we should not + // issue a second call to WSAConnect() +#if defined (Q_WS_WIN) + if (checkWrite && ((readyToWrite && *readyToWrite) || !readyToWrite) && ret > 0) { + setState(QAbstractSocket::ConnectedState); + d_func()->fetchConnectionParameters(); + return true; + } +#endif + if (ret == 0) { + if (timedOut) + *timedOut = true; + d->setError(QAbstractSocket::SocketTimeoutError, + QNativeSocketEnginePrivate::TimeOutErrorString); + return false; + } else if (state() == QAbstractSocket::ConnectingState) { + connectToHost(d->peerAddress, d->peerPort); + } + + return ret > 0; +} + +/*! + Returns the size of the operating system's socket receive + buffer. Depending on the operating system, this size may be + different from what has been set earlier with + setReceiveBufferSize(). +*/ +qint64 QNativeSocketEngine::receiveBufferSize() const +{ + Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::receiveBufferSize(), -1); + return option(ReceiveBufferSocketOption); +} + +/*! + Sets the size of the operating system receive buffer to \a size. + + For clients, this should be set before connectToHost() is called; + otherwise it will have no effect. For servers, it should be called + before listen(). + + The operating system receive buffer size effectively limits two + things: how much data can be in transit at any one moment, and how + much data can be received in one iteration of the main event loop. + Setting the size of the receive buffer may have an impact on the + socket's performance. + + The default value is operating system-dependent. +*/ +void QNativeSocketEngine::setReceiveBufferSize(qint64 size) +{ + Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::setReceiveBufferSize(), Q_VOID); + setOption(ReceiveBufferSocketOption, size); +} + +/*! + Returns the size of the operating system send buffer. Depending on + the operating system, this size may be different from what has + been set earlier with setSendBufferSize(). +*/ +qint64 QNativeSocketEngine::sendBufferSize() const +{ + Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::setSendBufferSize(), -1); + return option(SendBufferSocketOption); +} + +/*! + Sets the size of the operating system send buffer to \a size. + + The operating system send buffer size effectively limits how much + data can be in transit at any one moment. Setting the size of the + send buffer may have an impact on the socket's performance. + + The default value is operating system-dependent. +*/ +void QNativeSocketEngine::setSendBufferSize(qint64 size) +{ + Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::setSendBufferSize(), Q_VOID); + setOption(SendBufferSocketOption, size); +} + + +/*! + Sets the option \a option to the value \a value. +*/ +bool QNativeSocketEngine::setOption(SocketOption option, int value) +{ + Q_D(QNativeSocketEngine); + return d->setOption(option, value); +} + +/*! + Returns the value of the option \a socketOption. +*/ +int QNativeSocketEngine::option(SocketOption socketOption) const +{ + Q_D(const QNativeSocketEngine); + return d->option(socketOption); +} + +bool QNativeSocketEngine::isReadNotificationEnabled() const +{ + Q_D(const QNativeSocketEngine); + return d->readNotifier && d->readNotifier->isEnabled(); +} + +/* + \internal + \class QReadNotifier + \brief The QReadNotifer class is used to improve performance. + + QReadNotifier is a private class used for performance reasons vs + connecting to the QSocketNotifier activated() signal. + */ +class QReadNotifier : public QSocketNotifier +{ +public: + QReadNotifier(int fd, QNativeSocketEngine *parent) + : QSocketNotifier(fd, QSocketNotifier::Read, parent) + { engine = parent; } + +protected: + bool event(QEvent *); + + QNativeSocketEngine *engine; +}; + +bool QReadNotifier::event(QEvent *e) +{ + if (e->type() == QEvent::SockAct) { + engine->readNotification(); + return true; + } + return QSocketNotifier::event(e); +} + +/* + \internal + \class QWriteNotifier + \brief The QWriteNotifer class is used to improve performance. + + QWriteNotifier is a private class used for performance reasons vs + connecting to the QSocketNotifier activated() signal. + */ +class QWriteNotifier : public QSocketNotifier +{ +public: + QWriteNotifier(int fd, QNativeSocketEngine *parent) + : QSocketNotifier(fd, QSocketNotifier::Write, parent) { engine = parent; } + +protected: + bool event(QEvent *); + + QNativeSocketEngine *engine; +}; + +bool QWriteNotifier::event(QEvent *e) +{ + if (e->type() == QEvent::SockAct) { + if (engine->state() == QAbstractSocket::ConnectingState) + engine->connectionNotification(); + else + engine->writeNotification(); + return true; + } + return QSocketNotifier::event(e); +} + +class QExceptionNotifier : public QSocketNotifier +{ +public: + QExceptionNotifier(int fd, QNativeSocketEngine *parent) + : QSocketNotifier(fd, QSocketNotifier::Exception, parent) { engine = parent; } + +protected: + bool event(QEvent *); + + QNativeSocketEngine *engine; +}; + +bool QExceptionNotifier::event(QEvent *e) +{ + if (e->type() == QEvent::SockAct) { + engine->exceptionNotification(); + return true; + } + return QSocketNotifier::event(e); +} + +void QNativeSocketEngine::setReadNotificationEnabled(bool enable) +{ + Q_D(QNativeSocketEngine); + if (d->readNotifier) { + d->readNotifier->setEnabled(enable); + } else if (enable && d->threadData->eventDispatcher) { + d->readNotifier = new QReadNotifier(d->socketDescriptor, this); + d->readNotifier->setEnabled(true); + } +} + +bool QNativeSocketEngine::isWriteNotificationEnabled() const +{ + Q_D(const QNativeSocketEngine); + return d->writeNotifier && d->writeNotifier->isEnabled(); +} + +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); + } +} + +bool QNativeSocketEngine::isExceptionNotificationEnabled() const +{ + Q_D(const QNativeSocketEngine); + return d->exceptNotifier && d->exceptNotifier->isEnabled(); +} + +void QNativeSocketEngine::setExceptionNotificationEnabled(bool enable) +{ + Q_D(QNativeSocketEngine); + if (d->exceptNotifier) { + d->exceptNotifier->setEnabled(enable); + } else if (enable && d->threadData->eventDispatcher) { + d->exceptNotifier = new QExceptionNotifier(d->socketDescriptor, this); + d->exceptNotifier->setEnabled(true); + } +} + +QT_END_NAMESPACE |