From 6f6e34d204574fa32797b60ca90130e912a39bfd Mon Sep 17 00:00:00 2001 From: Raymond Wagner Date: Sun, 12 Feb 2012 02:17:25 -0500 Subject: [PATCH] Add ServerPool class. This adds a new ServerPool class to libmythbase, with the purpose of emulating the interfaces of both QTcpServer and QUdpSocket as is currently required by the various interfaces MythTV supports. This class replaces the previous use of QHostAddress::Any and QHostAddress::AnyIPv6 to perform blanket binds, with a pool of sockets each bound to individual addresses. The following signals are available for use: newConnection(QTcpSocket *) newDatagram(QByteArray, QHostAddress, quint16 port) Additionally, newTcpConnection(int) can be subclassed and overridden to produce alternate socket types. --- mythtv/libs/libmythbase/libmythbase.pro | 6 +- mythtv/libs/libmythbase/serverpool.cpp | 209 ++++++++++++++++++++++++ mythtv/libs/libmythbase/serverpool.h | 89 ++++++++++ 3 files changed, 301 insertions(+), 3 deletions(-) create mode 100644 mythtv/libs/libmythbase/serverpool.cpp create mode 100644 mythtv/libs/libmythbase/serverpool.h diff --git a/mythtv/libs/libmythbase/libmythbase.pro b/mythtv/libs/libmythbase/libmythbase.pro index 7049ebef4f3..a26bf12557b 100644 --- a/mythtv/libs/libmythbase/libmythbase.pro +++ b/mythtv/libs/libmythbase/libmythbase.pro @@ -23,7 +23,7 @@ HEADERS += unzip.h unzip_p.h zipentry_p.h iso639.h iso3166.h mythmedia.h HEADERS += util.h mythhdd.h mythcdrom.h autodeletedeque.h dbutil.h HEADERS += mythhttppool.h mythhttphandler.h mythdeque.h mythlogging.h HEADERS += mythbaseutil.h referencecounter.h version.h mythcommandlineparser.h -HEADERS += mythscheduler.h filesysteminfo.h hardwareprofile.h +HEADERS += mythscheduler.h filesysteminfo.h hardwareprofile.h serverpool.h SOURCES += mthread.cpp mthreadpool.cpp SOURCES += mythsocket.cpp mythsocketthread.cpp msocketdevice.cpp @@ -37,7 +37,7 @@ SOURCES += unzip.cpp iso639.cpp iso3166.cpp mythmedia.cpp util.cpp SOURCES += mythhdd.cpp mythcdrom.cpp dbutil.cpp SOURCES += mythhttppool.cpp mythhttphandler.cpp logging.cpp SOURCES += referencecounter.cpp mythcommandlineparser.cpp -SOURCES += filesysteminfo.cpp hardwareprofile.cpp +SOURCES += filesysteminfo.cpp hardwareprofile.cpp serverpool.cpp win32:SOURCES += msocketdevice_win.cpp unix { @@ -63,7 +63,7 @@ inc.files += mythcoreutil.h mythlocale.h mythdownloadmanager.h inc.files += mythtranslation.h iso639.h iso3166.h mythmedia.h util.h inc.files += mythcdrom.h autodeletedeque.h dbutil.h mythhttppool.h mythdeque.h inc.files += referencecounter.h mythcommandlineparser.h mthread.h mthreadpool.h -inc.files += filesysteminfo.h hardwareprofile.h bonjourregister.h +inc.files += filesysteminfo.h hardwareprofile.h bonjourregister.h serverpool.h # Allow both #include and #include inc2.path = $${PREFIX}/include/mythtv/libmythbase diff --git a/mythtv/libs/libmythbase/serverpool.cpp b/mythtv/libs/libmythbase/serverpool.cpp new file mode 100644 index 00000000000..4519560ec7e --- /dev/null +++ b/mythtv/libs/libmythbase/serverpool.cpp @@ -0,0 +1,209 @@ + +#include "mythcorecontext.h" +#include "mythlogging.h" +#include "serverpool.h" + +PrivTcpServer::PrivTcpServer(QObject *parent) : QTcpServer(parent) +{ +} + +void PrivTcpServer::incomingConnection(int socket) +{ + emit newConnection(socket); +} + +ServerPool::ServerPool(QObject *parent) : QObject(parent), + m_listening(false), m_maxPendingConn(30), m_port(0), + m_proxy(QNetworkProxy::DefaultProxy) +{ +} + +ServerPool::~ServerPool() +{ + close(); +} + +void ServerPool::close(void) +{ + QTcpServer *server; + while (!m_tcpServers.isEmpty()) + { + server = m_tcpServers.takeLast(); + server->disconnect(); + server->close(); + delete server; + } + + QUdpSocket *socket; + while (!m_udpSockets.isEmpty()) + { + socket = m_udpSockets.takeLast(); + socket->disconnect(); + socket->close(); + delete socket; + } +} + +bool ServerPool::listen(QList addrs, quint16 port, + bool requireall) +{ + m_port = port; + m_listening = true; + QList::const_iterator it; + + for (it = addrs.begin(); it != addrs.end(); ++it) + { + PrivTcpServer *server = new PrivTcpServer(this); + server->setProxy(m_proxy); + server->setMaxPendingConnections(m_maxPendingConn); + + connect(server, SIGNAL(newConnection(int)), + this, SLOT(newTcpConnection(int))); + + if (server->listen(*it, m_port)) + { + LOG(VB_GENERAL, LOG_INFO, QString("Listening on TCP %1:%2") + .arg(it->toString()).arg(port)); + m_tcpServers.append(server); + if (m_port == 0) + m_port = server->serverPort(); + } + else if (requireall) + { + LOG(VB_GENERAL, LOG_ERR, QString("Failed listening on TCP %1:%2") + .arg(it->toString()).arg(port)); + close(); + server->disconnect(); + delete server; + return false; + } + else + { + LOG(VB_GENERAL, LOG_WARNING, QString("Failed listening on TCP %1:%2") + .arg(it->toString()).arg(port)); + server->disconnect(); + delete server; + } + } + + if (m_tcpServers.size() == 0) + return false; + + return true; +} + +bool ServerPool::listen(QStringList addrstr, quint16 port, bool requireall) +{ + QList addrs; + QStringList::const_iterator it; + for (it = addrstr.begin(); it != addrstr.end(); ++it) + addrs << QHostAddress(*it); + return listen(addrs, port, requireall); +} + +bool ServerPool::listen(quint16 port, bool requireall) +{ + return listen(gCoreContext->MythHostAddress(), port, requireall); +} + +bool ServerPool::bind(QList addrs, quint16 port, + bool requireall) +{ + m_port = port; + m_listening = true; + QList::const_iterator it; + + for (it = addrs.begin(); it != addrs.end(); ++it) + { + QUdpSocket *socket = new QUdpSocket(this); + connect(socket, SIGNAL(readyRead()), + this, SLOT(newUdpDatagram())); + + if (socket->bind(*it, port)) + { + LOG(VB_GENERAL, LOG_INFO, QString("Binding to UDP %1:%2") + .arg(it->toString()).arg(port)); + m_udpSockets.append(socket); + } + else if (requireall) + { + LOG(VB_GENERAL, LOG_ERR, QString("Failed binding to UDP %1:%2") + .arg(it->toString()).arg(port)); + close(); + socket->disconnect(); + delete socket; + return false; + } + else + { + LOG(VB_GENERAL, LOG_WARNING, QString("Failed binding to UDP %1:%2") + .arg(it->toString()).arg(port)); + socket->disconnect(); + delete socket; + } + } + + if (m_udpSockets.size() == 0) + return false; + + return true; +} + +bool ServerPool::bind(QStringList addrstr, quint16 port, bool requireall) +{ + QList addrs; + QStringList::const_iterator it; + for (it = addrstr.begin(); it != addrstr.end(); ++it) + addrs << QHostAddress(*it); + return bind(addrs, port, requireall); +} + +bool ServerPool::bind(quint16 port, bool requireall) +{ + return bind(gCoreContext->MythHostAddress(), port, requireall); +} + +qint64 ServerPool::writeDatagram(const char * data, qint64 size, + const QHostAddress &addr, quint16 port) +{ + if (!m_listening || m_udpSockets.isEmpty()) + { + LOG(VB_GENERAL, LOG_ERR, "Trying to write datagram to disconnected " + "ServerPool instance."); + return -1; + } + + QUdpSocket *socket = m_udpSockets.first(); + return socket->writeDatagram(data, size, addr, port); +} + +qint64 ServerPool::writeDatagram(const QByteArray &datagram, + const QHostAddress &addr, quint16 port) +{ + return writeDatagram(datagram.data(), datagram.size(), addr, port); +} + +void ServerPool::newTcpConnection(int socket) +{ + QTcpSocket *qsock = new QTcpSocket(this); + qsock->setSocketDescriptor(socket); + emit newConnection(qsock); +} + +void ServerPool::newUdpDatagram(void) +{ + QUdpSocket *socket = dynamic_cast(sender()); + + while (socket->state() == QAbstractSocket::BoundState && + socket->hasPendingDatagrams()) + { + QByteArray buffer; + buffer.resize(socket->pendingDatagramSize()); + QHostAddress sender; + quint16 senderPort; + + socket->readDatagram(buffer.data(), buffer.size(), + &sender, &senderPort); + emit newDatagram(buffer, sender, senderPort); + } +} diff --git a/mythtv/libs/libmythbase/serverpool.h b/mythtv/libs/libmythbase/serverpool.h new file mode 100644 index 00000000000..a544b0e0134 --- /dev/null +++ b/mythtv/libs/libmythbase/serverpool.h @@ -0,0 +1,89 @@ +#ifndef SERVERPOOL_H_ +#define SERVERPOOL_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "mythbaseexp.h" + +/** \class ServerPool + * \brief Manages a collection of sockets listening on different ports. + * + * This class allows a TCP or UDP server to listen on a list of addresses + * rather than limited to a single or all addresses. This is done by opening + * a separate server for each defined QHostAddress, and signalling + * collectively for any new connections. + * + * This can be subclassed with new 'newTcpConnection()' and 'newConnection()' + * methods to allow signalling for alternate socket types. + */ + +class PrivTcpServer : public QTcpServer +{ + Q_OBJECT + public: + PrivTcpServer(QObject *parent = 0); + ~PrivTcpServer() {}; + + signals: + void newConnection(int socket); + + protected: + void incomingConnection(int socket); +}; + +class MBASE_PUBLIC ServerPool : public QObject +{ + Q_OBJECT + + public: + ServerPool(QObject *parent=0); + ~ServerPool(void); + + bool listen(QList addrs, quint16 port, bool requireall=true); + bool listen(QStringList addrs, quint16 port, bool requireall=true); + bool listen(quint16 port, bool requireall=true); + + bool bind(QList addrs, quint16 port, bool requireall=true); + bool bind(QStringList addrs, quint16 port, bool requireall=true); + bool bind(quint16 port, bool requireall=true); + + qint64 writeDatagram(const char * data, qint64 size, + const QHostAddress &addr, quint16 port); + qint64 writeDatagram(const QByteArray &datagram, + const QHostAddress &addr, quint16 port); + + bool isListening(void) { return m_listening; } + int maxPendingConnections(void) { return m_maxPendingConn; } + void setMaxPendingConnections(int n) { m_maxPendingConn = n; } + quint16 serverPort(void) { return m_port; } + + QNetworkProxy proxy(void) { return m_proxy; } + void setProxy(const QNetworkProxy &proxy) { m_proxy = proxy; } + + void close(void); + + signals: + void newConnection(QTcpSocket *); + void newDatagram(QByteArray, QHostAddress, quint16); + + protected slots: + virtual void newUdpDatagram(void); + virtual void newTcpConnection(int socket); + + private: + bool m_listening; + int m_maxPendingConn; + quint16 m_port; + QNetworkProxy m_proxy; + + QList m_tcpServers; + QList m_udpSockets; +}; + +#endif