Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
Showing
3 changed files
with
301 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |