src/network/socket/qtcpserver.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
child 7 f7bc934e204c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/network/socket/qtcpserver.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,648 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//#define QTCPSERVER_DEBUG
+
+/*! \class QTcpServer
+
+    \brief The QTcpServer class provides a TCP-based server.
+
+    \reentrant
+    \ingroup network
+    \inmodule QtNetwork
+
+    This class makes it possible to accept incoming TCP connections.
+    You can specify the port or have QTcpServer pick one
+    automatically. You can listen on a specific address or on all the
+    machine's addresses.
+
+    Call listen() to have the server listen for incoming connections.
+    The newConnection() signal is then emitted each time a client
+    connects to the server.
+
+    Call nextPendingConnection() to accept the pending connection as
+    a connected QTcpSocket. The function returns a pointer to a
+    QTcpSocket in QAbstractSocket::ConnectedState that you can use for
+    communicating with the client.
+
+    If an error occurs, serverError() returns the type of error, and
+    errorString() can be called to get a human readable description of
+    what happened.
+
+    When listening for connections, the address and port on which the
+    server is listening are available as serverAddress() and
+    serverPort().
+
+    Calling close() makes QTcpServer stop listening for incoming
+    connections.
+
+    Although QTcpServer is mostly designed for use with an event
+    loop, it's possible to use it without one. In that case, you must
+    use waitForNewConnection(), which blocks until either a
+    connection is available or a timeout expires.
+
+    \sa QTcpSocket, {Fortune Server Example}, {Threaded Fortune Server Example},
+        {Loopback Example}, {Torrent Example}
+*/
+
+/*! \fn void QTcpServer::newConnection()
+
+    This signal is emitted every time a new connection is available.
+
+    \sa hasPendingConnections(), nextPendingConnection()
+*/
+
+#include "private/qobject_p.h"
+#include "qalgorithms.h"
+#include "qhostaddress.h"
+#include "qlist.h"
+#include "qpointer.h"
+#include "qnativesocketengine_p.h"
+#include "qtcpserver.h"
+#include "qtcpsocket.h"
+#include "qnetworkproxy.h"
+
+QT_BEGIN_NAMESPACE
+
+#define Q_CHECK_SOCKETENGINE(returnValue) do { \
+    if (!d->socketEngine) { \
+        return returnValue; \
+    } } while (0)
+
+class QTcpServerPrivate : public QObjectPrivate, public QAbstractSocketEngineReceiver
+{
+    Q_DECLARE_PUBLIC(QTcpServer)
+public:
+    QTcpServerPrivate();
+    ~QTcpServerPrivate();
+
+    QList<QTcpSocket *> pendingConnections;
+
+    quint16 port;
+    QHostAddress address;
+
+    QAbstractSocket::SocketState state;
+    QAbstractSocketEngine *socketEngine;
+
+    QAbstractSocket::SocketError serverSocketError;
+    QString serverSocketErrorString;
+
+    int maxConnections;
+
+#ifndef QT_NO_NETWORKPROXY
+    QNetworkProxy proxy;
+    QNetworkProxy resolveProxy(const QHostAddress &address, quint16 port);
+#endif
+
+    // from QAbstractSocketEngineReceiver
+    void readNotification();
+    inline void writeNotification() {}
+    inline void exceptionNotification() {}
+    inline void connectionNotification() {}
+#ifndef QT_NO_NETWORKPROXY
+    inline void proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *) {}
+#endif
+
+};
+
+/*! \internal
+*/
+QTcpServerPrivate::QTcpServerPrivate()
+ : port(0)
+ , state(QAbstractSocket::UnconnectedState)
+ , socketEngine(0)
+ , serverSocketError(QAbstractSocket::UnknownSocketError)
+ , maxConnections(30)
+{
+}
+
+/*! \internal
+*/
+QTcpServerPrivate::~QTcpServerPrivate()
+{
+}
+
+#ifndef QT_NO_NETWORKPROXY
+/*! \internal
+
+    Resolve the proxy to its final value.
+*/
+QNetworkProxy QTcpServerPrivate::resolveProxy(const QHostAddress &address, quint16 port)
+{
+    if (address == QHostAddress::LocalHost ||
+        address == QHostAddress::LocalHostIPv6)
+        return QNetworkProxy::NoProxy;
+
+    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(port, QString(), QNetworkProxyQuery::TcpServer);
+        proxies = QNetworkProxyFactory::proxyForQuery(query);
+    }
+
+    // return the first that we can use
+    foreach (const QNetworkProxy &p, proxies) {
+        if (p.capabilities() & QNetworkProxy::ListeningCapability)
+            return p;
+    }
+
+    // no proxy found
+    // DefaultProxy will raise an error
+    return QNetworkProxy(QNetworkProxy::DefaultProxy);
+}
+#endif
+
+/*! \internal
+*/
+void QTcpServerPrivate::readNotification()
+{
+    Q_Q(QTcpServer);
+    for (;;) {
+        if (pendingConnections.count() >= maxConnections) {
+#if defined (QTCPSERVER_DEBUG)
+            qDebug("QTcpServerPrivate::_q_processIncomingConnection() too many connections");
+#endif
+            if (socketEngine->isReadNotificationEnabled())
+                socketEngine->setReadNotificationEnabled(false);
+            return;
+        }
+
+        int descriptor = socketEngine->accept();
+        if (descriptor == -1)
+            break;
+#if defined (QTCPSERVER_DEBUG)
+        qDebug("QTcpServerPrivate::_q_processIncomingConnection() accepted socket %i", descriptor);
+#endif
+        q->incomingConnection(descriptor);
+
+        QPointer<QTcpServer> that = q;
+        emit q->newConnection();
+        if (!that || !q->isListening())
+            return;
+    }
+}
+
+/*!
+    Constructs a QTcpServer object.
+
+    \a parent is passed to the QObject constructor.
+
+    \sa listen(), setSocketDescriptor()
+*/
+QTcpServer::QTcpServer(QObject *parent)
+    : QObject(*new QTcpServerPrivate, parent)
+{
+}
+
+/*!
+    Destroys the QTcpServer object. If the server is listening for
+    connections, the socket is automatically closed.
+
+    Any client \l{QTcpSocket}s that are still connected must either
+    disconnect or be reparented before the server is deleted.
+
+    \sa close()
+*/
+QTcpServer::~QTcpServer()
+{
+    close();
+}
+
+/*!
+    Tells the server to listen for incoming connections on address \a
+    address and port \a port. If \a port is 0, a port is chosen
+    automatically. If \a address is QHostAddress::Any, the server
+    will listen on all network interfaces.
+
+    Returns true on success; otherwise returns false.
+
+    \sa isListening()
+*/
+bool QTcpServer::listen(const QHostAddress &address, quint16 port)
+{
+    Q_D(QTcpServer);
+    if (d->state == QAbstractSocket::ListeningState) {
+        qWarning("QTcpServer::listen() called when already listening");
+        return false;
+    }
+
+    QAbstractSocket::NetworkLayerProtocol proto = address.protocol();
+
+#ifdef QT_NO_NETWORKPROXY
+    static const QNetworkProxy &proxy = *(QNetworkProxy *)0;
+#else
+    QNetworkProxy proxy = d->resolveProxy(address, port);
+#endif
+
+    delete d->socketEngine;
+    d->socketEngine = QAbstractSocketEngine::createSocketEngine(QAbstractSocket::TcpSocket, proxy, this);
+    if (!d->socketEngine) {
+        d->serverSocketError = QAbstractSocket::UnsupportedSocketOperationError;
+        d->serverSocketErrorString = tr("Operation on socket is not supported");
+        return false;
+    }
+    if (!d->socketEngine->initialize(QAbstractSocket::TcpSocket, proto)) {
+        d->serverSocketError = d->socketEngine->error();
+        d->serverSocketErrorString = d->socketEngine->errorString();
+        return false;
+    }
+
+#if defined(Q_OS_UNIX)
+    // Under Unix, we want to be able to bind to the port, even if a socket on
+    // the same address-port is in TIME_WAIT. Under Windows this is possible
+    // anyway -- furthermore, the meaning of reusable on Windows is different:
+    // it means that you can use the same address-port for multiple listening
+    // sockets.
+    // Don't abort though if we can't set that option. For example the socks
+    // engine doesn't support that option, but that shouldn't prevent us from
+    // trying to bind/listen.
+    d->socketEngine->setOption(QAbstractSocketEngine::AddressReusable, 1);
+#endif
+
+    if (!d->socketEngine->bind(address, port)) {
+        d->serverSocketError = d->socketEngine->error();
+        d->serverSocketErrorString = d->socketEngine->errorString();
+        return false;
+    }
+
+    if (!d->socketEngine->listen()) {
+        d->serverSocketError = d->socketEngine->error();
+        d->serverSocketErrorString = d->socketEngine->errorString();
+        return false;
+    }
+
+    d->socketEngine->setReceiver(d);
+    d->socketEngine->setReadNotificationEnabled(true);
+
+    d->state = QAbstractSocket::ListeningState;
+    d->address = d->socketEngine->localAddress();
+    d->port = d->socketEngine->localPort();
+
+#if defined (QTCPSERVER_DEBUG)
+    qDebug("QTcpServer::listen(%i, \"%s\") == true (listening on port %i)", port,
+           address.toString().toLatin1().constData(), d->socketEngine->localPort());
+#endif
+    return true;
+}
+
+/*!
+    Returns true if the server is currently listening for incoming
+    connections; otherwise returns false.
+
+    \sa listen()
+*/
+bool QTcpServer::isListening() const
+{
+    Q_D(const QTcpServer);
+    Q_CHECK_SOCKETENGINE(false);
+    return d->socketEngine->state() == QAbstractSocket::ListeningState;
+}
+
+/*!
+    Closes the server. The server will no longer listen for incoming
+    connections.
+
+    \sa listen()
+*/
+void QTcpServer::close()
+{
+    Q_D(QTcpServer);
+
+    qDeleteAll(d->pendingConnections);
+    d->pendingConnections.clear();
+
+    if (d->socketEngine) {
+        d->socketEngine->close();
+        QT_TRY {
+            d->socketEngine->deleteLater();
+        } QT_CATCH(const std::bad_alloc &) {
+            // in out of memory situations, the socketEngine
+            // will be deleted in ~QTcpServer (it's a child-object of this)
+        }
+        d->socketEngine = 0;
+    }
+
+    d->state = QAbstractSocket::UnconnectedState;
+}
+
+/*!
+    Returns the native socket descriptor the server uses to listen
+    for incoming instructions, or -1 if the server is not listening.
+
+    If the server is using QNetworkProxy, the returned descriptor may
+    not be usable with native socket functions.
+
+    \sa setSocketDescriptor(), isListening()
+*/
+int QTcpServer::socketDescriptor() const
+{
+    Q_D(const QTcpServer);
+    Q_CHECK_SOCKETENGINE(-1);
+    return d->socketEngine->socketDescriptor();
+}
+
+/*!
+    Sets the socket descriptor this server should use when listening
+    for incoming connections to \a socketDescriptor. Returns true if
+    the socket is set successfully; otherwise returns false.
+
+    The socket is assumed to be in listening state.
+
+    \sa socketDescriptor(), isListening()
+*/
+bool QTcpServer::setSocketDescriptor(int socketDescriptor)
+{
+    Q_D(QTcpServer);
+    if (isListening()) {
+        qWarning("QTcpServer::setSocketDescriptor() called when already listening");
+        return false;
+    }
+
+    if (d->socketEngine)
+        delete d->socketEngine;
+    d->socketEngine = QAbstractSocketEngine::createSocketEngine(socketDescriptor, this);
+    if (!d->socketEngine->initialize(socketDescriptor, QAbstractSocket::ListeningState)) {
+        d->serverSocketError = d->socketEngine->error();
+        d->serverSocketErrorString = d->socketEngine->errorString();
+#if defined (QTCPSERVER_DEBUG)
+        qDebug("QTcpServer::setSocketDescriptor(%i) failed (%s)", socketDescriptor,
+               d->serverSocketErrorString.toLatin1().constData());
+#endif
+        return false;
+    }
+
+    d->socketEngine->setReceiver(d);
+    d->socketEngine->setReadNotificationEnabled(true);
+
+    d->state = d->socketEngine->state();
+    d->address = d->socketEngine->localAddress();
+    d->port = d->socketEngine->localPort();
+
+#if defined (QTCPSERVER_DEBUG)
+    qDebug("QTcpServer::setSocketDescriptor(%i) succeeded.", socketDescriptor);
+#endif
+    return true;
+}
+
+/*!
+    Returns the server's port if the server is listening for
+    connections; otherwise returns 0.
+
+    \sa serverAddress(), listen()
+*/
+quint16 QTcpServer::serverPort() const
+{
+    Q_D(const QTcpServer);
+    Q_CHECK_SOCKETENGINE(0);
+    return d->socketEngine->localPort();
+}
+
+/*!
+    Returns the server's address if the server is listening for
+    connections; otherwise returns QHostAddress::Null.
+
+    \sa serverPort(), listen()
+*/
+QHostAddress QTcpServer::serverAddress() const
+{
+    Q_D(const QTcpServer);
+    Q_CHECK_SOCKETENGINE(QHostAddress(QHostAddress::Null));
+    return d->socketEngine->localAddress();
+}
+
+/*!
+    Waits for at most \a msec milliseconds or until an incoming
+    connection is available. Returns true if a connection is
+    available; otherwise returns false. If the operation timed out
+    and \a timedOut is not 0, *\a timedOut will be set to true.
+
+    This is a blocking function call. Its use is disadvised in a
+    single-threaded GUI application, since the whole application will
+    stop responding until the function returns.
+    waitForNewConnection() is mostly useful when there is no event
+    loop available.
+
+    The non-blocking alternative is to connect to the newConnection()
+    signal.
+
+    If msec is -1, this function will not time out.
+
+    \sa hasPendingConnections(), nextPendingConnection()
+*/
+bool QTcpServer::waitForNewConnection(int msec, bool *timedOut)
+{
+    Q_D(QTcpServer);
+    if (d->state != QAbstractSocket::ListeningState)
+        return false;
+
+    if (!d->socketEngine->waitForRead(msec, timedOut)) {
+        d->serverSocketError = d->socketEngine->error();
+        d->serverSocketErrorString = d->socketEngine->errorString();
+        return false;
+    }
+
+    if (timedOut && *timedOut)
+        return false;
+
+    d->readNotification();
+
+    return true;
+}
+
+/*!
+    Returns true if the server has a pending connection; otherwise
+    returns false.
+
+    \sa nextPendingConnection(), setMaxPendingConnections()
+*/
+bool QTcpServer::hasPendingConnections() const
+{
+    return !d_func()->pendingConnections.isEmpty();
+}
+
+/*!
+    Returns the next pending connection as a connected QTcpSocket
+    object.
+
+    The socket is created as a child of the server, which means that
+    it is automatically deleted when the QTcpServer object is
+    destroyed. It is still a good idea to delete the object
+    explicitly when you are done with it, to avoid wasting memory.
+
+    0 is returned if this function is called when there are no pending
+    connections.
+
+    \sa hasPendingConnections()
+*/
+QTcpSocket *QTcpServer::nextPendingConnection()
+{
+    Q_D(QTcpServer);
+    if (d->pendingConnections.isEmpty())
+        return 0;
+
+    if (!d->socketEngine->isReadNotificationEnabled())
+        d->socketEngine->setReadNotificationEnabled(true);
+
+    return d->pendingConnections.takeFirst();
+}
+
+/*!
+    This virtual function is called by QTcpServer when a new
+    connection is available. The \a socketDescriptor argument is the
+    native socket descriptor for the accepted connection.
+
+    The base implementation creates a QTcpSocket, sets the socket
+    descriptor and then stores the QTcpSocket in an internal list of
+    pending connections. Finally newConnection() is emitted.
+
+    Reimplement this function to alter the server's behavior when a
+    connection is available.
+
+    If this server is using QNetworkProxy then the \a socketDescriptor
+    may not be usable with native socket functions, and should only be
+    used with QTcpSocket::setSocketDescriptor().
+
+    \sa newConnection(), nextPendingConnection()
+*/
+void QTcpServer::incomingConnection(int socketDescriptor)
+{
+#if defined (QTCPSERVER_DEBUG)
+    qDebug("QTcpServer::incomingConnection(%i)", socketDescriptor);
+#endif
+
+    QTcpSocket *socket = new QTcpSocket(this);
+    socket->setSocketDescriptor(socketDescriptor);
+    d_func()->pendingConnections.append(socket);
+}
+
+/*!
+    Sets the maximum number of pending accepted connections to \a
+    numConnections. QTcpServer will accept no more than \a
+    numConnections incoming connections before
+    nextPendingConnection() is called. By default, the limit is 30
+    pending connections.
+
+    Clients may still able to connect after the server has reached
+    its maximum number of pending connections (i.e., QTcpSocket can
+    still emit the connected() signal). QTcpServer will stop
+    accepting the new connections, but the operating system may
+    still keep them in queue.
+
+    \sa maxPendingConnections(), hasPendingConnections()
+*/
+void QTcpServer::setMaxPendingConnections(int numConnections)
+{
+    d_func()->maxConnections = numConnections;
+}
+
+/*!
+    Returns the maximum number of pending accepted connections. The
+    default is 30.
+
+    \sa setMaxPendingConnections(), hasPendingConnections()
+*/
+int QTcpServer::maxPendingConnections() const
+{
+    return d_func()->maxConnections;
+}
+
+/*!
+    Returns an error code for the last error that occurred.
+
+    \sa errorString()
+*/
+QAbstractSocket::SocketError QTcpServer::serverError() const
+{
+    return d_func()->serverSocketError;
+}
+
+/*!
+    Returns a human readable description of the last error that
+    occurred.
+
+    \sa serverError()
+*/
+QString QTcpServer::errorString() const
+{
+    return d_func()->serverSocketErrorString;
+}
+
+#ifndef QT_NO_NETWORKPROXY
+/*!
+    \since 4.1
+
+    Sets the explicit network proxy for this socket to \a networkProxy.
+
+    To disable the use of a proxy for this socket, use the
+    QNetworkProxy::NoProxy proxy type:
+
+    \snippet doc/src/snippets/code/src_network_socket_qtcpserver.cpp 0
+
+    \sa proxy(), QNetworkProxy
+*/
+void QTcpServer::setProxy(const QNetworkProxy &networkProxy)
+{
+    Q_D(QTcpServer);
+    d->proxy = networkProxy;
+}
+
+/*!
+    \since 4.1
+
+    Returns the network proxy for this socket.
+    By default QNetworkProxy::DefaultProxy is used.
+
+    \sa setProxy(), QNetworkProxy
+*/
+QNetworkProxy QTcpServer::proxy() const
+{
+    Q_D(const QTcpServer);
+    return d->proxy;
+}
+#endif // QT_NO_NETWORKPROXY
+
+QT_END_NAMESPACE
+
+#include "moc_qtcpserver.cpp"
+