#include "tcpserver.h" #include #include "tcpserver_logger.h" #include "workerbase.h" #include #include #include #include #include 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 workers; void *mUserData; unsigned short port; bool bUseThreads; static QHash s_servers; static CommandFactory s_cbFactory; }; QHash 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 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(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(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; }