/**************************************************************************** ** Copyright (c) 2025 Evgeny Teterin (nayk) ** All right reserved. ** ** Permission is hereby granted, free of charge, to any person obtaining ** a copy of this software and associated documentation files (the ** "Software"), to deal in the Software without restriction, including ** without limitation the rights to use, copy, modify, merge, publish, ** distribute, sublicense, and/or sell copies of the Software, and to ** permit persons to whom the Software is furnished to do so, subject to ** the following conditions: ** ** The above copyright notice and this permission notice shall be ** included in all copies or substantial portions of the Software. ** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE ** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION ** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION ** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ** ****************************************************************************/ #include "main_window.h" #include "./ui_main_window.h" #include #include #include #include #include #include "application_config.h" namespace server { //=========================================================== //============================================================================== MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); initialize(); updateControls(); } //============================================================================== MainWindow::~MainWindow() { disconnectClients(); server.close(); delete ui; } //============================================================================== void MainWindow::initialize() { setWindowTitle( QApplication::applicationName() ); Application::initializeLog(ui->textEditLog); connect(ui->pushButtonConnect, &QPushButton::clicked, this, &MainWindow::btnConnect); connect(&server, &QTcpServer::newConnection, this, &MainWindow::newConnection); } //============================================================================== void MainWindow::updateControls() { bool connect {server.isListening()}; ui->spinBoxPort->setEnabled(!connect); ui->pushButtonConnect->setText(connect ? tr("Закрыть") : tr("Открыть")); } //============================================================================== void MainWindow::disconnectClients() { while (!clients.isEmpty()) { QTcpSocket *socket = clients.takeLast(); if (socket) { socket->close(); if (socket->state() != QAbstractSocket::UnconnectedState) socket->waitForDisconnected(2000); } } } //============================================================================== void MainWindow::btnConnect() { ui->pushButtonConnect->setEnabled(false); ui->spinBoxPort->setEnabled(false); QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); if (server.isListening()) { disconnectClients(); server.close(); Application::addToLog( ui->textEditLog, tr("Сервер закрыт"), Application::LogTypeWarning); ui->textEditLog->append(""); } else { if (server.listen( QHostAddress::Any, static_cast(ui->spinBoxPort->value()) ) ) { Application::addToLog( ui->textEditLog, tr("Сервер открыт для входящих подключений на порту %1") .arg(ui->spinBoxPort->value()), Application::LogTypeWarning); } else { Application::addToLog( ui->textEditLog, tr("Не удалось открыть сервер (%1)").arg(server.errorString()), Application::LogTypeError); } } ui->pushButtonConnect->setEnabled(true); updateControls(); } //============================================================================== void MainWindow::newConnection() { QTcpSocket* socket = server.nextPendingConnection(); if (!socket) return; clients.append(socket); connect(socket, &QTcpSocket::readyRead, this, &MainWindow::clientRead); connect(socket, &QTcpSocket::disconnected, this, &MainWindow::clientDisconnect); QHostAddress clientAddress = socket->peerAddress(); quint16 clientPort = socket->peerPort(); Application::addToLog( ui->textEditLog, tr("Входящее подключение. Адрес клиента: %1; Порт клиента: %2") .arg(clientAddress.toString()).arg(clientPort), Application::LogTypeWarning); } //============================================================================== void MainWindow::clientRead() { QTcpSocket* socket = qobject_cast(sender()); if (!socket) return; QHostAddress clientAddress = socket->peerAddress(); Application::addToLog( ui->textEditLog, tr("Чтение входящих данных с %1").arg(clientAddress.toString())); QByteArray readData; QElapsedTimer timer; timer.start(); while (readData.isEmpty() && (timer.elapsed() < timeout)) { readData = socket->readAll(); } if (readData.isEmpty()) { Application::addToLog( ui->textEditLog, tr("Таймаут ожидания данных с %1").arg(clientAddress.toString()), Application::LogTypeError); return; } Application::addToLog( ui->textEditLog, tr("Принятые данные c %1: %2 (%3 байт)") .arg(clientAddress.toString(), QString(readData.toHex(' '))) .arg(readData.size()), Application::LogTypeData); Application::addToLog( ui->textEditLog, tr("Обработка массива %1 байт").arg(readData.size())); QByteArray sendData; // Преобразование данных в выходной массив: // если значение 0x00 или 0xFF, заменить на 0xAA // иначе на четных индексах увеличить значение на 1 // на нечетных уменьшить значение на 1 for (auto i = 0; i < readData.size(); ++i) { quint8 byte = static_cast( readData.at(i) ); if ((byte == 0) || (byte == 0xFF)) byte = 0xAA; else if ((i % 2) == 0) ++byte; else --byte; sendData.append( static_cast(byte) ); } Application::addToLog( ui->textEditLog, tr("Данные после обработки: %1") .arg( QString(sendData.toHex(' ')) ), Application::LogTypeData ); Application::addToLog( ui->textEditLog, tr("Отправка данных клиенту %1").arg(clientAddress.toString())); qsizetype sendCount {0}; timer.restart(); while ((sendCount < sendData.size()) && (timer.elapsed() < timeout)) { qsizetype count = socket->write(sendData.mid( static_cast(sendCount) )); if (count < 0) { Application::addToLog( ui->textEditLog, tr("Ошибка отправки данных на %1") .arg(clientAddress.toString()), Application::LogTypeError ); } else if (count > 0) { Application::addToLog( ui->textEditLog, tr("Отправка данных на %1: %2 (%3 байт)") .arg(clientAddress.toString(), QString(sendData.mid( static_cast(sendCount), static_cast(count) ).toHex(' '))) .arg(count), Application::LogTypeData ); sendCount += count; } } if (sendCount < sendData.size()) { Application::addToLog( ui->textEditLog, tr("Таймаут отправки данных на %1. Всего отправлено %2 байт из %3") .arg(clientAddress.toString()).arg(sendCount).arg(sendData.size()), Application::LogTypeError ); } } //============================================================================== void MainWindow::clientDisconnect() { QString clientAddressStr; if (sender()) { QTcpSocket* socket = qobject_cast(sender()); if (socket) { QHostAddress clientAddress = socket->peerAddress(); clientAddressStr = clientAddress.toString(); } } Application::addToLog( ui->textEditLog, tr("Закрытие входящего подключения %1").arg(clientAddressStr), Application::LogTypeWarning); } //============================================================================== } // mamespace //===============================================================