From 5cd7c782ced83c630401facd15600eba0736754d Mon Sep 17 00:00:00 2001 From: Carl Philipp Klemm Date: Mon, 30 Mar 2026 16:58:52 +0200 Subject: [PATCH] Refactor server services to share more code --- CMakeLists.txt | 2 + src/service/server.cpp | 93 +++++++++++++++++++++++++++++++++ src/service/server.h | 49 +++++++++++++++++ src/service/service.cpp | 5 +- src/service/tcpclient.cpp | 2 +- src/service/tcpserver.cpp | 84 ++++++----------------------- src/service/tcpserver.h | 15 +----- src/service/websocketserver.cpp | 83 +++++++++++++++++++++++++++++ src/service/websocketserver.h | 32 ++++++++++++ 9 files changed, 280 insertions(+), 85 deletions(-) create mode 100644 src/service/server.cpp create mode 100644 src/service/server.h create mode 100644 src/service/websocketserver.cpp create mode 100644 src/service/websocketserver.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b461509..ea99bbe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,6 +49,8 @@ target_sources(SHinterface src/service/service.cpp src/service/tcpclient.h src/service/tcpclient.cpp + src/service/server.h + src/service/server.cpp src/service/tcpserver.h src/service/tcpserver.cpp src/service/websocketserver.h diff --git a/src/service/server.cpp b/src/service/server.cpp new file mode 100644 index 0000000..e01ba4c --- /dev/null +++ b/src/service/server.cpp @@ -0,0 +1,93 @@ +#include +#include +#include + +#include "items/item.h" +#include "service.h" +#include "server.h" + +Server::Server(QObject* parent) + : Service(parent) +{ +} + +Server::~Server() +{ +} + +void Server::processIncomeingJson(const QByteArray& jsonbytes) +{ + QJsonDocument doc = QJsonDocument::fromJson(jsonbytes); + QJsonObject json = doc.object(); + QString type = json["MessageType"].toString(); + if(type == "ItemUpdate") + { + QJsonArray data = json["Data"].toArray(); + bool FullList = json["FullList"].toBool(false); + std::vector> items; + for(QJsonValueRef itemjson : data) + { + QJsonObject jsonobject = itemjson.toObject(); + std::shared_ptr item = Item::loadItem(jsonobject); + if(item) + { + qDebug()<<"Server got item"<getName(); + item->setLoaded(FullList); + items.push_back(item); + } + } + if(FullList && !items.empty()) + { + requestReplaceItems(items); + sigRequestSave(); + } + else if(!items.empty()) + { + gotItems(items, false); + } + } + else + { + Service::processIncomeingJson(jsonbytes); + } +} + +void Server::handleSocketError() +{ + QObject* obj = sender(); + if (obj) { + removeClient(static_cast(obj)); + } +} + +void Server::handleSocketDisconnect() +{ + QObject* obj = sender(); + if (obj) { + removeClient(static_cast(obj)); + } +} + +void Server::removeClient(QTcpSocket* socket) +{ + for(size_t i = 0; i < clients.size(); i++) + { + if(clients[i].socket.tcpSocket == socket) + { + clients.erase(clients.begin() + i); + --i; + } + } +} + +void Server::removeClient(QWebSocket* socket) +{ + for(size_t i = 0; i < clients.size(); i++) + { + if(clients[i].socket.webSocket == socket) + { + clients.erase(clients.begin() + i); + --i; + } + } +} diff --git a/src/service/server.h b/src/service/server.h new file mode 100644 index 0000000..6c27062 --- /dev/null +++ b/src/service/server.h @@ -0,0 +1,49 @@ +#ifndef SERVER_BASE_H +#define SERVER_BASE_H + +#include +#include +#include + +#include "service.h" + +class Server : public Service +{ + Q_OBJECT + +protected: + typedef enum + { + STATE_IDLE, + STATE_RECV_JSON, + } client_state_t; + + struct Client + { + union { + QTcpSocket* tcpSocket; + QWebSocket* webSocket; + } socket; + QByteArray buffer; + client_state_t state = STATE_IDLE; + long long recievebytes = 0; + }; + + std::vector clients; + +public: + Server(QObject* parent = nullptr); + virtual ~Server(); + +protected: + virtual void processIncomeingJson(const QByteArray& jsonbytes) override; + void handleSocketError(); + void handleSocketDisconnect(); + void removeClient(QTcpSocket* socket); + void removeClient(QWebSocket* socket); + +signals: + void sigRequestSave(); +}; + +#endif // SERVER_BASE_H diff --git a/src/service/service.cpp b/src/service/service.cpp index 5129512..eb27ee0 100644 --- a/src/service/service.cpp +++ b/src/service/service.cpp @@ -1,7 +1,6 @@ #include #include #include -#include #include "items/item.h" #include "items/itemstore.h" @@ -32,7 +31,7 @@ void Service::sensorEvent(Sensor sensor) void Service::itemUpdated(std::weak_ptr item) { - qDebug()<<__func__; + qDebug()<<"Service sending item"<getName(); QJsonArray items; QJsonObject itemjson; item.lock()->store(itemjson); @@ -78,7 +77,6 @@ void Service::sendItems() void Service::processIncomeingJson(const QByteArray& jsonbytes) { QJsonDocument doc = QJsonDocument::fromJson(jsonbytes); - qDebug()<<"Got Json:"<> items; for(QJsonValueRef itemjson : data) @@ -42,6 +41,7 @@ void TcpClient::processIncomeingJson(const QByteArray& jsonbytes) std::shared_ptr item = Item::loadItem(jsonobject); if(item) { + qDebug()<<"Client got item"<getName(); item->setLoaded(false); items.push_back(item); } diff --git a/src/service/tcpserver.cpp b/src/service/tcpserver.cpp index 0cdeed8..ed1b620 100644 --- a/src/service/tcpserver.cpp +++ b/src/service/tcpserver.cpp @@ -4,11 +4,11 @@ #include #include "items/item.h" -#include "service.h" +#include "server.h" #include "tcpserver.h" TcpServer::TcpServer(QObject* parent): - Service(parent), + Server(parent), server(this) { connect(&server, &QTcpServer::newConnection, this, &TcpServer::incomingConnection); @@ -16,48 +16,18 @@ TcpServer::TcpServer(QObject* parent): void TcpServer::sendJson(const QJsonObject& json) { - for(auto client: clients) + for(auto& client: clients) { - QByteArray jsonData = QJsonDocument(json).toJson(); - client.socket->write(QString("MSG JSON LEN " + QString::number(jsonData.size()) + "\n").toLatin1() + jsonData); + if(client.socket.tcpSocket) { + QByteArray jsonData = QJsonDocument(json).toJson(); + client.socket.tcpSocket->write(QString("MSG JSON LEN " + QString::number(jsonData.size()) + "\n").toLatin1() + jsonData); + } } } void TcpServer::processIncomeingJson(const QByteArray& jsonbytes) { - QJsonDocument doc = QJsonDocument::fromJson(jsonbytes); - QJsonObject json = doc.object(); - QString type = json["MessageType"].toString(); - if(type == "ItemUpdate") - { - qDebug()<<"Got Items"; - QJsonArray data = json["Data"].toArray(); - bool FullList = json["FullList"].toBool(false); - std::vector> items; - for(QJsonValueRef itemjson : data) - { - QJsonObject jsonobject = itemjson.toObject(); - std::shared_ptr item = Item::loadItem(jsonobject); - if(item) - { - item->setLoaded(FullList); - items.push_back(item); - } - } - if(FullList && !items.empty()) - { - requestReplaceItems(items); - sigRequestSave(); - } - else if(!items.empty()) - { - gotItems(items, false); - } - } - else - { - Service::processIncomeingJson(jsonbytes); - } + Server::processIncomeingJson(jsonbytes); } bool TcpServer::launch(const QHostAddress &address, quint16 port) @@ -74,38 +44,16 @@ void TcpServer::incomingConnection() qDebug()<<"Got new client from"<peerAddress().toString(); if(client) { - clients.push_back({client}); - connect(client, &QTcpSocket::errorOccurred, this, &TcpServer::socketError); - connect(client, &QTcpSocket::disconnected, this, &TcpServer::socketDisconnect); + Client c; + c.socket.tcpSocket = client; + clients.push_back(c); + connect(client, &QTcpSocket::errorOccurred, this, [this, client]() { removeClient(client); }); + connect(client, &QTcpSocket::disconnected, this, [this, client]() { removeClient(client); }); connect(client, &QTcpSocket::readyRead, this, &TcpServer::socketReadyRead); } } } -void TcpServer::socketError(QAbstractSocket::SocketError socketError) -{ - (void)socketError; - for(size_t i = 0; i < clients.size(); i++) - { - if(clients[i].socket == TcpServer::sender()) - { - clients.erase(clients.begin()+i); - --i; - } - } -} -void TcpServer::socketDisconnect() -{ - for(size_t i = 0; i < clients.size(); i++) - { - if(clients[i].socket == TcpServer::sender()) - { - clients.erase(clients.begin()+i); - --i; - } - } -} - void TcpServer::processComand(const QByteArray& command, Client& client) { if(command.startsWith("MSG JSON LEN ")) @@ -120,15 +68,15 @@ void TcpServer::socketReadyRead() { for(size_t i = 0; i < clients.size(); i++) { - if(clients[i].socket == sender()) + QTcpSocket* tcp = clients[i].socket.tcpSocket; + if(tcp && tcp == sender()) { - QByteArray newChars = clients[i].socket->readAll(); + QByteArray newChars = tcp->readAll(); clients[i].buffer += newChars; bool remianing = true; while(remianing) { - qDebug()< #include -#include "service.h" +#include "server.h" -class TcpServer : public Service +class TcpServer : public Server { Q_OBJECT - struct Client - { - QTcpSocket* socket; - QByteArray buffer; - client_state_t state = STATE_IDLE; - long long recievebytes = 0; - }; - - std::vector clients; QTcpServer server; public: @@ -31,8 +22,6 @@ signals: private slots: void incomingConnection(); - void socketError(QAbstractSocket::SocketError socketError); - void socketDisconnect(); void socketReadyRead(); protected: diff --git a/src/service/websocketserver.cpp b/src/service/websocketserver.cpp new file mode 100644 index 0000000..2a34bc1 --- /dev/null +++ b/src/service/websocketserver.cpp @@ -0,0 +1,83 @@ +#include +#include +#include +#include + +#include "items/item.h" +#include "server.h" +#include "websocketserver.h" + +WebSocketServer::WebSocketServer(const QString &serverName, QObject* parent) + : Server(parent), + server(serverName, QWebSocketServer::NonSecureMode, this) +{ + connect(&server, &QWebSocketServer::newConnection, this, &WebSocketServer::incomingConnection); +} + +WebSocketServer::~WebSocketServer() +{ + // Clean up WebSocket clients (they need special handling) + for(auto& client : clients) + { + if(client.socket.webSocket) + { + client.socket.webSocket->close(); + delete client.socket.webSocket; + } + } +} + +void WebSocketServer::sendJson(const QJsonObject& json) +{ + QByteArray jsonData = QJsonDocument(json).toJson(); + for(auto& client : clients) + { + if(client.socket.webSocket && client.socket.webSocket->state() == QAbstractSocket::ConnectedState) + { + client.socket.webSocket->sendTextMessage(QString::fromUtf8(jsonData)); + } + } +} + +void WebSocketServer::processIncomeingJson(const QByteArray& jsonbytes) +{ + // Validate JSON first (WebSockets may receive malformed data) + QJsonDocument doc = QJsonDocument::fromJson(jsonbytes); + if(!doc.isObject()) + { + qWarning() << "Invalid JSON received:" << QString::fromUtf8(jsonbytes); + return; + } + + Server::processIncomeingJson(jsonbytes); +} + +bool WebSocketServer::launch(const QHostAddress &address, quint16 port) +{ + qInfo()<<"WebSocket server launched on"<peerAddress().toString(); + if(client) + { + Client c; + c.socket.webSocket = client; + clients.push_back(c); + connect(client, &QWebSocket::errorOccurred, this, [this, client]() { removeClient(client); }); + connect(client, &QWebSocket::disconnected, this, [this, client]() { removeClient(client); }); + connect(client, &QWebSocket::textMessageReceived, this, &WebSocketServer::textMessageReceived); + } + } +} + +void WebSocketServer::textMessageReceived(const QString &message) +{ + QByteArray jsonData = message.toUtf8(); + processIncomeingJson(jsonData); +} diff --git a/src/service/websocketserver.h b/src/service/websocketserver.h new file mode 100644 index 0000000..73d74de --- /dev/null +++ b/src/service/websocketserver.h @@ -0,0 +1,32 @@ +#ifndef WEBSOCKETSERVER_SERVER_H +#define WEBSOCKETSERVER_SERVER_H + +#include +#include + +#include "server.h" + +class WebSocketServer : public Server +{ + Q_OBJECT + + QWebSocketServer server; + +public: + WebSocketServer(const QString &serverName, QObject* parent = nullptr); + virtual ~WebSocketServer(); + virtual bool launch(const QHostAddress &address = QHostAddress::Any, quint16 port = 0) override; + virtual void sendJson(const QJsonObject& json) override; + +private slots: + void incomingConnection(); + void textMessageReceived(const QString &message); + +protected: + virtual void processIncomeingJson(const QByteArray& jsonbytes) override; + +signals: + void sigRequestSave(); +}; + +#endif // WEBSOCKETSERVER_SERVER_H