298 lines
9.5 KiB
C++
298 lines
9.5 KiB
C++
/****************************************************************************
|
||
** Copyright (c) 2025 Evgeny Teterin (nayk) <nayk@nxt.ru>
|
||
** 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 <QtWidgets/QApplication>
|
||
#include <QtGui/QRegularExpressionValidator>
|
||
#include <QtNetwork/QHostAddress>
|
||
#include <QtCore/QDateTime>
|
||
#include <QtCore/QElapsedTimer>
|
||
|
||
#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<quint16>(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<QTcpSocket*>(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<quint8>( readData.at(i) );
|
||
|
||
if ((byte == 0) || (byte == 0xFF))
|
||
byte = 0xAA;
|
||
else if ((i % 2) == 0)
|
||
++byte;
|
||
else
|
||
--byte;
|
||
|
||
sendData.append( static_cast<char>(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<int>(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<int>(sendCount),
|
||
static_cast<int>(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<QTcpSocket*>(sender());
|
||
|
||
if (socket) {
|
||
|
||
QHostAddress clientAddress = socket->peerAddress();
|
||
clientAddressStr = clientAddress.toString();
|
||
}
|
||
}
|
||
|
||
Application::addToLog(
|
||
ui->textEditLog,
|
||
tr("Закрытие входящего подключения %1").arg(clientAddressStr),
|
||
Application::LogTypeWarning);
|
||
}
|
||
//==============================================================================
|
||
|
||
} // mamespace //===============================================================
|
||
|