211 lines
5.0 KiB
C++
Executable File
211 lines
5.0 KiB
C++
Executable File
|
|
|
|
|
|
#include "tcpserver.h"
|
|
#include <utility>
|
|
#include "tcpserver_logger.h"
|
|
#include "workerbase.h"
|
|
|
|
#include <QtNetwork/QTcpSocket>
|
|
#include <QtCore/QCoreApplication>
|
|
#include <QtCore/QThread>
|
|
#include <QtCore/QTimer>
|
|
|
|
#include <NGLLogger/NGLLoggerBootstrap.h>
|
|
|
|
NGL::INGLLogger &getTcpServerLogger()
|
|
{
|
|
static NGL::INGLLogger *tcpserver_logger_ptr = nullptr;
|
|
if (!tcpserver_logger_ptr)
|
|
tcpserver_logger_ptr = &NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("tcpserver");
|
|
return *tcpserver_logger_ptr;
|
|
}
|
|
|
|
typedef QHash< unsigned short, TcpServer::CommandInterfaceList > CommandFactory;
|
|
class TcpServer::Private
|
|
{
|
|
public:
|
|
explicit Private(unsigned short port_)
|
|
: mUserData(nullptr)
|
|
, port(port_)
|
|
, bUseThreads(false)
|
|
{}
|
|
~Private()
|
|
{
|
|
const auto copy(workers);
|
|
for (WorkerInstance *worker : copy)
|
|
{
|
|
|
|
QThread *workerThread = worker->thread();
|
|
if (workerThread != qApp->thread())
|
|
{
|
|
QTimer::singleShot(0, worker, &WorkerInstance::deleteLater);
|
|
workerThread->wait();
|
|
}
|
|
else
|
|
delete worker;
|
|
}
|
|
workers.clear();
|
|
}
|
|
|
|
QVector<WorkerInstance *> workers;
|
|
void *mUserData;
|
|
unsigned short port;
|
|
bool bUseThreads;
|
|
|
|
static QHash<int, TcpServer *> s_servers;
|
|
static CommandFactory s_cbFactory;
|
|
};
|
|
QHash<int, TcpServer *> TcpServer::Private::s_servers;
|
|
CommandFactory TcpServer::Private::s_cbFactory;
|
|
|
|
TcpServer::TcpServer(unsigned short port)
|
|
: QTcpServer()
|
|
, d(new Private(port))
|
|
{
|
|
TCPSERVER_TRACE2("Port: " << port);
|
|
}
|
|
|
|
TcpServer::~TcpServer()
|
|
{
|
|
TCPSERVER_TRACE
|
|
Private::s_servers.remove(d->port);
|
|
delete d;
|
|
}
|
|
|
|
TcpServer *TcpServer::instance(unsigned short port)
|
|
{
|
|
if (!Private::s_servers.contains(port))
|
|
Private::s_servers.insert(port, new TcpServer(port));
|
|
|
|
return Private::s_servers[ port ];
|
|
}
|
|
|
|
bool TcpServer::listen(const QHostAddress &address)
|
|
{
|
|
TCPSERVER_TRACE2("address: " << address);
|
|
return QTcpServer::listen(address, d->port);
|
|
}
|
|
|
|
void TcpServer::setUseWorkerThreads(bool bEnable)
|
|
{
|
|
TCPSERVER_TRACE2("bEnable: " << bEnable);
|
|
d->bUseThreads = bEnable;
|
|
}
|
|
|
|
bool TcpServer::useWorkerThreads() const
|
|
{
|
|
return d->bUseThreads;
|
|
}
|
|
|
|
QVector<WorkerInstance *> TcpServer::workers() const
|
|
{
|
|
return d->workers;
|
|
}
|
|
|
|
unsigned short TcpServer::getListenPort() const
|
|
{
|
|
return d->port;
|
|
}
|
|
|
|
bool TcpServer::registerCommand(unsigned short port, CommandInterface * (*factory)(unsigned short))
|
|
{
|
|
CommandInterfaceList &factoryList = Private::s_cbFactory[port];
|
|
if (factoryList.contains(factory))
|
|
{
|
|
TCPSERVER_ERROR("Factory " << (void *)factory << " already registered!")
|
|
return false;
|
|
}
|
|
factoryList.append(factory);
|
|
TCPSERVER_DEBUG("Added Factory " << (void *)factory << " for port " << port)
|
|
return true;
|
|
}
|
|
|
|
bool TcpServer::unregisterCommand(unsigned short port, CommandInterface * (*factory)(unsigned short))
|
|
{
|
|
CommandInterfaceList &factoryList = Private::s_cbFactory[port];
|
|
if (!factoryList.contains(factory))
|
|
{
|
|
TCPSERVER_WARN("Trying to unregister factory " << (void *)factory << " for port " << port << " which is not registered!")
|
|
return false;
|
|
}
|
|
TCPSERVER_INFO("Removed Factory " << (void *)factory << " for port " << port)
|
|
factoryList.removeAll(factory);
|
|
return true;
|
|
}
|
|
|
|
TcpServer::CommandInterfaceList TcpServer::registeredCommands(unsigned short port)
|
|
{
|
|
return Private::s_cbFactory[port];
|
|
}
|
|
|
|
void TcpServer::incomingConnection(qintptr socketDescriptor)
|
|
{
|
|
|
|
WorkerInstance *worker = new WorkerInstance(socketDescriptor, d->port);
|
|
connect(worker, &WorkerInstance::finished, this, &TcpServer::workerFinished);
|
|
d->workers += worker;
|
|
|
|
if (d->bUseThreads)
|
|
{
|
|
QThread *workerThread = new QThread(this);
|
|
|
|
connect(workerThread, &QThread::started, worker, &WorkerInstance::doWork);
|
|
connect(workerThread, &QThread::finished, worker, &WorkerInstance::quit);
|
|
worker->moveToThread(workerThread);
|
|
|
|
workerThread->start();
|
|
|
|
|
|
connect(workerThread, &QThread::finished, workerThread, &QThread::deleteLater);
|
|
TCPSERVER_DEBUG("Created new WorkerThread " << worker)
|
|
}
|
|
else
|
|
{
|
|
QTimer::singleShot(0, worker, &WorkerInstance::doWork);
|
|
TCPSERVER_DEBUG("Created new WorkerInstance " << worker)
|
|
}
|
|
}
|
|
|
|
void TcpServer::workerFinished()
|
|
{
|
|
WorkerInstance *wi = qobject_cast<WorkerInstance *>(sender());
|
|
if (wi)
|
|
{
|
|
if (!d->workers.removeAll(wi))
|
|
TCPSERVER_WARN("Got a signal that a worker exited, but could not find it in our list!")
|
|
wi->deleteLater();
|
|
}
|
|
else
|
|
{
|
|
TCPSERVER_WARN("Got a signal that a worker exited, but it's neither a WorkerInstance nor WorkerThread!")
|
|
}
|
|
}
|
|
|
|
void TcpServer::shutdownRequest()
|
|
{
|
|
emit aboutToQuit(d->port);
|
|
for (WorkerBase *worker : std::as_const(d->workers))
|
|
{
|
|
WorkerInstance *wi = dynamic_cast<WorkerInstance *>(worker);
|
|
if (wi)
|
|
{
|
|
wi->quit();
|
|
while (!wi->isFinished())
|
|
QCoreApplication::processEvents();
|
|
}
|
|
}
|
|
close();
|
|
deleteLater();
|
|
}
|
|
|
|
void TcpServer::setUserData(void *userData)
|
|
{
|
|
d->mUserData = userData;
|
|
}
|
|
|
|
void *TcpServer::userData() const
|
|
{
|
|
return d->mUserData;
|
|
}
|