Skip to content

Commit

Permalink
Add ServerPool class.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
wagnerrp committed Feb 12, 2012
1 parent fb37a67 commit 6f6e34d
Show file tree
Hide file tree
Showing 3 changed files with 301 additions and 3 deletions.
6 changes: 3 additions & 3 deletions mythtv/libs/libmythbase/libmythbase.pro
Expand Up @@ -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
Expand All @@ -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 {
Expand All @@ -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 <blah.h> and #include <libmythbase/blah.h>
inc2.path = $${PREFIX}/include/mythtv/libmythbase
Expand Down
209 changes: 209 additions & 0 deletions 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<QHostAddress> addrs, quint16 port,
bool requireall)
{
m_port = port;
m_listening = true;
QList<QHostAddress>::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<QHostAddress> 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<QHostAddress> addrs, quint16 port,
bool requireall)
{
m_port = port;
m_listening = true;
QList<QHostAddress>::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<QHostAddress> 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<QUdpSocket*>(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);
}
}
89 changes: 89 additions & 0 deletions mythtv/libs/libmythbase/serverpool.h
@@ -0,0 +1,89 @@
#ifndef SERVERPOOL_H_
#define SERVERPOOL_H_

#include <QList>
#include <QHostAddress>
#include <QNetworkProxy>
#include <QTcpServer>
#include <QTcpSocket>
#include <QUdpSocket>
#include <QStringList>

#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<QHostAddress> 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<QHostAddress> 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<PrivTcpServer*> m_tcpServers;
QList<QUdpSocket*> m_udpSockets;
};

#endif

0 comments on commit 6f6e34d

Please sign in to comment.