/** * @file EchoServer.cpp * @brief Mock XPL2 server for a single port. */ #include "EchoServer.h" #include EchoServer::EchoServer (quint16 port, const char *name, QObject *parent) : QTcpServer (parent), m_name (name), m_port (port) { connect (this, &QTcpServer::newConnection, this, &EchoServer::onNewConnection); connect (&m_pingTimer, &QTimer::timeout, this, &EchoServer::sendKaPing); if (!listen (QHostAddress::Any, port)) qCritical ("Failed to listen on %s port %d: %s", m_name, m_port, qPrintable (errorString ())); else { qInfo ("Listening on %s port %d", m_name, m_port); m_pingTimer.start (1000); } } void EchoServer::onNewConnection () { while (auto *sock = nextPendingConnection ()) { qInfo ("[%s:%d] client connected", m_name, m_port); m_clients.append (sock); connect (sock, &QTcpSocket::readyRead, this, &EchoServer::onClientMessageReady); connect (sock, &QTcpSocket::disconnected, this, &EchoServer::onClientDisconnected); } } void EchoServer::onClientMessageReady () { auto *sock = qobject_cast (sender ()); while (sock->canReadLine ()) { QByteArray line = sock->readLine (); handleCommand (sock, line); } } void EchoServer::onClientDisconnected () { auto *sock = qobject_cast (sender ()); qInfo ("[%s:%d] client disconnected", m_name, m_port); m_clients.removeOne (sock); sock->deleteLater (); } void EchoServer::sendKaPing () { for (auto *client : m_clients) { if (client->state () == QAbstractSocket::ConnectedState) client->write ("KA_PING\n"); } } void EchoServer::handleCommand (QTcpSocket *client, const QByteArray &line) { QByteArray trimmed = line.trimmed (); if (trimmed.isEmpty ()) return; /* Split on first comma to get command token. */ int comma = trimmed.indexOf (','); QByteArray cmd = (comma >= 0) ? trimmed.left (comma) : trimmed; if (cmd == "KA_PING") { qDebug ("[%s:%d] KA_PING ACK received", m_name, m_port); return; } if (cmd == "GS_JC_VERSION") { qInfo ("[%s:%d] -> GS_JC_VERSION reply", m_name, m_port); client->write ("GS_JC_VERSION,1,\"1.05\",\"2.00\",15\n"); return; } qWarning ("[%s:%d] Unknown command: %s", m_name, m_port, trimmed.constData ()); }