diff --git a/app/app.pri b/app/app.pri index 007ea3671b589a5f7d6bb913cc28d0bb02a12c24..21388a6a3eab19e9f25988883e0f9348f2d16a11 100644 --- a/app/app.pri +++ b/app/app.pri @@ -1,10 +1,12 @@ QT += \ core \ - network + network \ + websockets QT -= gui HEADERS += \ + $${PWD}/src/websockets/websocketserver.h \ $${PWD}/src/handlers/confighandler.h \ $${PWD}/src/handlers/systemhandler.h \ $${PWD}/src/handlers/ocshandler.h \ @@ -12,6 +14,7 @@ HEADERS += \ SOURCES += \ $${PWD}/src/main.cpp \ + $${PWD}/src/websockets/websocketserver.cpp \ $${PWD}/src/handlers/confighandler.cpp \ $${PWD}/src/handlers/systemhandler.cpp \ $${PWD}/src/handlers/ocshandler.cpp \ diff --git a/app/src/main.cpp b/app/src/main.cpp index 23fe103dec3d427b52043d5265b2ff7f5a2e50c3..a702c0219fd173d6a2ed3b00740b59896e85e86f 100644 --- a/app/src/main.cpp +++ b/app/src/main.cpp @@ -1,20 +1,24 @@ +#include <QStringList> #include <QJsonObject> //#include <QTranslator> //#include <QLocale> #include <QCommandLineParser> +#include <QCommandLineOption> #include <QCoreApplication> +#include <QDebug> #include "handlers/confighandler.h" -#include "handlers/systemhandler.h" -#include "handlers/ocshandler.h" -#include "handlers/itemhandler.h" +//#include "handlers/systemhandler.h" +//#include "handlers/ocshandler.h" +//#include "handlers/itemhandler.h" +#include "websockets/websocketserver.h" int main(int argc, char *argv[]) { // Init QCoreApplication app(argc, argv); - ConfigHandler *configHandler = new ConfigHandler(); + ConfigHandler *configHandler = new ConfigHandler(&app); QJsonObject appConfigApplication = configHandler->getAppConfigApplication(); app.setApplicationName(appConfigApplication["name"].toString()); @@ -33,7 +37,27 @@ int main(int argc, char *argv[]) clParser.setApplicationDescription(appConfigApplication["description"].toString()); clParser.addHelpOption(); clParser.addVersionOption(); + + // Port 49152-61000 will available as ephemeral port + // https://en.wikipedia.org/wiki/Ephemeral_port + QCommandLineOption clOptionPort(QStringList() << "p" << "port", "Port for websocket server [default: 49152].", "port", "49152"); + clParser.addOption(clOptionPort); + clParser.process(app); + int port = clParser.value(clOptionPort).toInt(); + + // Setup websocket server + WebSocketServer *wsServer = new WebSocketServer(appConfigApplication["id"].toString(), port, &app); + QObject::connect(wsServer, &WebSocketServer::stopped, &app, &QCoreApplication::quit); + + if (wsServer->start()) { + qDebug() << "Websocket server started at:" << wsServer->serverUrl().toString(); + } + else { + qCritical() << "Failed to start websocket server:" << wsServer->errorString(); + return 1; + } + return app.exec(); } diff --git a/app/src/websockets/websocketserver.cpp b/app/src/websockets/websocketserver.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c1aed4614b6c43989ab2a531f1f54fe459c088bf --- /dev/null +++ b/app/src/websockets/websocketserver.cpp @@ -0,0 +1,94 @@ +#include "websocketserver.h" + +#include <QHostAddress> +#include <QWebSocketServer> +#include <QWebSocket> + +#include "qtlib_json.h" + +WebSocketServer::WebSocketServer(const QString &serverName, quint16 serverPort, QObject *parent) + : QObject(parent), serverName_(serverName), serverPort_(serverPort) +{ + wsServer_ = new QWebSocketServer(serverName_, QWebSocketServer::NonSecureMode, this); + connect(wsServer_, &QWebSocketServer::newConnection, this, &WebSocketServer::wsNewConnection); + connect(wsServer_, &QWebSocketServer::closed, this, &WebSocketServer::stopped); +} + +WebSocketServer::~WebSocketServer() +{ + stop(); + wsServer_->deleteLater(); +} + +bool WebSocketServer::start() +{ + if (wsServer_->listen(QHostAddress::Any, serverPort_)) { + emit started(); + return true; + } + return false; +} + +void WebSocketServer::stop() +{ + wsServer_->close(); +} + +bool WebSocketServer::isError() +{ + if (wsServer_->error() != QWebSocketProtocol::CloseCodeNormal) { + return true; + } + return false; +} + +QString WebSocketServer::errorString() +{ + return wsServer_->errorString(); +} + +QUrl WebSocketServer::serverUrl() +{ + return wsServer_->serverUrl(); +} + +void WebSocketServer::wsNewConnection() +{ + QWebSocket *wsClient = wsServer_->nextPendingConnection(); + connect(wsClient, &QWebSocket::binaryMessageReceived, this, &WebSocketServer::wsBinaryMessageReceived); + connect(wsClient, &QWebSocket::textMessageReceived, this, &WebSocketServer::wsTextMessageReceived); + connect(wsClient, &QWebSocket::disconnected, this, &WebSocketServer::wsDisconnected); + wsClients_ << wsClient; +} + +void WebSocketServer::wsBinaryMessageReceived(const QByteArray &message) +{ + QWebSocket *wsClient = qobject_cast<QWebSocket *>(sender()); + if (wsClient) { + wsClient->sendBinaryMessage(message); + } +} + +void WebSocketServer::wsTextMessageReceived(const QString &message) +{ + QWebSocket *wsClient = qobject_cast<QWebSocket *>(sender()); + if (wsClient) { + qtlib::Json json(message.toUtf8()); + if (json.isValid()) { + QJsonObject jsonObject = json.toObject(); + if (jsonObject["method"].toString() == "stop") { + stop(); + } + } + wsClient->sendTextMessage(message); + } +} + +void WebSocketServer::wsDisconnected() +{ + QWebSocket *wsClient = qobject_cast<QWebSocket *>(sender()); + if (wsClient) { + wsClients_.removeAll(wsClient); + wsClient->deleteLater(); + } +} diff --git a/app/src/websockets/websocketserver.h b/app/src/websockets/websocketserver.h new file mode 100644 index 0000000000000000000000000000000000000000..01d4591ba9792cb4699d7405699f94f623312fd6 --- /dev/null +++ b/app/src/websockets/websocketserver.h @@ -0,0 +1,39 @@ +#pragma once + +#include <QObject> +#include <QUrl> + +class QWebSocketServer; +class QWebSocket; + +class WebSocketServer : public QObject +{ + Q_OBJECT + +public: + explicit WebSocketServer(const QString &serverName, quint16 serverPort = 0, QObject *parent = 0); + ~WebSocketServer(); + +signals: + void started(); + void stopped(); + +public slots: + bool start(); + void stop(); + bool isError(); + QString errorString(); + QUrl serverUrl(); + +private slots: + void wsNewConnection(); + void wsBinaryMessageReceived(const QByteArray &message); + void wsTextMessageReceived(const QString &message); + void wsDisconnected(); + +private: + QString serverName_; + quint16 serverPort_; + QWebSocketServer *wsServer_; + QList<QWebSocket *> wsClients_; +};