Реализация клиента и сервера
parent
c58fa4e814
commit
c637706c22
21
README.md
21
README.md
|
@ -1,3 +1,22 @@
|
||||||
# TcpSocket_Client_Server_Example
|
# TcpSocket_Client_Server_Example
|
||||||
|
|
||||||
Пример реализации клиента и сервера для обмена данными по TCP/IP
|
Пример реализации клиента и сервера для обмена данными по TCP/IP.
|
||||||
|
|
||||||
|
## Тестовое задание
|
||||||
|
|
||||||
|
Реализовать на Qt сервер, который будет принимать входящие соединения по TCP на определенном порту.
|
||||||
|
Реализовать следующие функции:
|
||||||
|
- открытие и закрытие входящего порта сервера;
|
||||||
|
- чтение входящего пакета от клиента в виде массива байт;
|
||||||
|
- обработка полученного массива по алгоритму (см. ниже);
|
||||||
|
- отправка нового массива обратно клиенту.
|
||||||
|
|
||||||
|
Алгоритм обработки входящего массива:
|
||||||
|
- если значение очередного байта 0x00 или 0xFF, заменить на 0xAA;
|
||||||
|
- иначе на четных индексах увеличить значение на 1;
|
||||||
|
- на нечетных позициях уменьшить значение на 1.
|
||||||
|
|
||||||
|
Приложение может быть реализовано в виде ПО с графическим интерфейсом, либо в виде консольного приложения.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -29,11 +29,21 @@
|
||||||
#include <QtCore/QDebug>
|
#include <QtCore/QDebug>
|
||||||
#include <QtCore/QDir>
|
#include <QtCore/QDir>
|
||||||
#include <QtCore/QStringList>
|
#include <QtCore/QStringList>
|
||||||
|
#include <QtCore/QDateTime>
|
||||||
|
|
||||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||||
# include <QtCore/QTextCodec>
|
# include <QtCore/QTextCodec>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
const char * clDefault[2] { "#111416", "#fdfdfd" };
|
||||||
|
const char * clLogDate[2] { "#929292", "#333333" };
|
||||||
|
const char * clLogInf[2] { "#ffffff", "#000000" };
|
||||||
|
const char * clLogWrn[2] { "#ff9c54", "#8d3c00" };
|
||||||
|
const char * clLogErr[2] { "#ff4040", "#a50000" };
|
||||||
|
const char * clLogIn[2] { "#55d864", "#003706" };
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
void Application::initialize()
|
void Application::initialize()
|
||||||
{
|
{
|
||||||
|
@ -104,3 +114,57 @@ void Application::installTranslations(const QString &lng)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
void Application::initializeLog(QTextEdit *textEdit)
|
||||||
|
{
|
||||||
|
if (!textEdit)
|
||||||
|
return;
|
||||||
|
|
||||||
|
textEdit->setStyleSheet(
|
||||||
|
QString(
|
||||||
|
"QTextEdit { "
|
||||||
|
"color: %1; "
|
||||||
|
"background-color: %2; "
|
||||||
|
"font-family: Courier New, Cascadia Mono, Lucida Console, Monospace; "
|
||||||
|
"font-size: 10pt; "
|
||||||
|
"}"
|
||||||
|
)
|
||||||
|
.arg( clDefault[ 1 ], clDefault[ 0 ] )
|
||||||
|
);
|
||||||
|
textEdit->document()->setMaximumBlockCount(10000);
|
||||||
|
}
|
||||||
|
//==============================================================================
|
||||||
|
void Application::addToLog(QTextEdit *textEdit, const QString &text, LogType logType)
|
||||||
|
{
|
||||||
|
if (!textEdit)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const QString line {"<font color=\"%1\">[%2]</font> <font color=\"%3\">%4</font>"};
|
||||||
|
|
||||||
|
QString cl = QString(clLogInf[ 0 ]);
|
||||||
|
|
||||||
|
switch (logType) {
|
||||||
|
case LogTypeError:
|
||||||
|
cl = QString(clLogErr[ 0 ]);
|
||||||
|
break;
|
||||||
|
case LogTypeWarning:
|
||||||
|
cl = QString(clLogWrn[ 0 ]);
|
||||||
|
break;
|
||||||
|
case LogTypeData:
|
||||||
|
cl = QString(clLogIn[ 0 ]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
textEdit->append(
|
||||||
|
line.arg(
|
||||||
|
QString(clLogDate[ 0 ]),
|
||||||
|
QDateTime::currentDateTime().toString("HH:mm:ss"),
|
||||||
|
cl,
|
||||||
|
text.toHtmlEscaped()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
|
||||||
|
}
|
||||||
|
//==============================================================================
|
||||||
|
|
|
@ -27,17 +27,26 @@
|
||||||
#define APP_CONFIG_H
|
#define APP_CONFIG_H
|
||||||
|
|
||||||
#include <QtCore/QString>
|
#include <QtCore/QString>
|
||||||
|
#include <QtWidgets/QTextEdit>
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
class Application
|
class Application
|
||||||
{
|
{
|
||||||
Q_DISABLE_COPY(Application)
|
Q_DISABLE_COPY(Application)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
enum LogType {
|
||||||
|
LogTypeInfo,
|
||||||
|
LogTypeData,
|
||||||
|
LogTypeError,
|
||||||
|
LogTypeWarning
|
||||||
|
};
|
||||||
|
|
||||||
static void initialize();
|
static void initialize();
|
||||||
static QString applicationRootPath();
|
static QString applicationRootPath();
|
||||||
static void installTranslations(const QString &lng = "ru");
|
static void installTranslations(const QString &lng = "ru");
|
||||||
|
static void initializeLog(QTextEdit *textEdit);
|
||||||
|
static void addToLog(QTextEdit *textEdit, const QString &text, LogType logType = LogTypeInfo);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Application() = delete;
|
Application() = delete;
|
||||||
|
|
|
@ -24,7 +24,14 @@
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
#include "main_window.h"
|
#include "main_window.h"
|
||||||
#include "./ui_main_window.h"
|
#include "./ui_main_window.h"
|
||||||
|
|
||||||
#include <QtWidgets/QApplication>
|
#include <QtWidgets/QApplication>
|
||||||
|
#include <QtGui/QRegularExpressionValidator>
|
||||||
|
#include <QtNetwork/QHostAddress>
|
||||||
|
#include <QtCore/QDateTime>
|
||||||
|
#include <QtCore/QElapsedTimer>
|
||||||
|
|
||||||
|
#include "application_config.h"
|
||||||
|
|
||||||
namespace client { //===========================================================
|
namespace client { //===========================================================
|
||||||
|
|
||||||
|
@ -34,13 +41,244 @@ MainWindow::MainWindow(QWidget *parent)
|
||||||
, ui(new Ui::MainWindow)
|
, ui(new Ui::MainWindow)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
setWindowTitle( QApplication::applicationName() );
|
|
||||||
|
initialize();
|
||||||
|
updateControls();
|
||||||
}
|
}
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
MainWindow::~MainWindow()
|
MainWindow::~MainWindow()
|
||||||
{
|
{
|
||||||
|
socket.disconnectFromHost();
|
||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
void MainWindow::initialize()
|
||||||
|
{
|
||||||
|
setWindowTitle( QApplication::applicationName() );
|
||||||
|
Application::initializeLog(ui->textEditLog);
|
||||||
|
|
||||||
|
connect(ui->pushButtonConnect, &QPushButton::clicked, this, &MainWindow::btnConnect);
|
||||||
|
connect(ui->pushButtonSend, &QPushButton::clicked, this, &MainWindow::btnSend);
|
||||||
|
connect(&socket, &QTcpSocket::connected, this, &MainWindow::socketConnected);
|
||||||
|
connect(&socket, &QTcpSocket::disconnected, this, &MainWindow::socketDisconnected);
|
||||||
|
|
||||||
|
QString ipRange {"(?:[0-1]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])"};
|
||||||
|
QRegularExpression ipRegex ("^" + ipRange
|
||||||
|
+ "\\." + ipRange
|
||||||
|
+ "\\." + ipRange
|
||||||
|
+ "\\." + ipRange + "$");
|
||||||
|
|
||||||
|
ui->lineEditIp->setValidator( new QRegularExpressionValidator(ipRegex, this) );
|
||||||
|
ui->lineEditIp->setText("127.0.0.1");
|
||||||
|
|
||||||
|
QRegularExpression hexRegex("([a-fA-F0-9]{2})([\\s][a-fA-F0-9]{2})*[\\s]?");
|
||||||
|
ui->lineEditData->setValidator( new QRegularExpressionValidator(hexRegex, this) );
|
||||||
|
}
|
||||||
|
//==============================================================================
|
||||||
|
void MainWindow::updateControls()
|
||||||
|
{
|
||||||
|
bool connect = isConnected();
|
||||||
|
|
||||||
|
ui->pushButtonSend->setEnabled(connect);
|
||||||
|
ui->lineEditIp->setEnabled(!connect);
|
||||||
|
ui->spinBoxPort->setEnabled(!connect);
|
||||||
|
|
||||||
|
ui->pushButtonConnect->setText(connect ? tr("Закрыть") : tr("Открыть"));
|
||||||
|
}
|
||||||
|
//==============================================================================
|
||||||
|
bool MainWindow::isConnected() const
|
||||||
|
{
|
||||||
|
return socket.state() == QTcpSocket::SocketState::ConnectedState;
|
||||||
|
}
|
||||||
|
//==============================================================================
|
||||||
|
void MainWindow::btnConnect()
|
||||||
|
{
|
||||||
|
ui->pushButtonConnect->setEnabled(false);
|
||||||
|
ui->pushButtonSend->setEnabled(false);
|
||||||
|
ui->lineEditData->setEnabled(false);
|
||||||
|
ui->lineEditIp->setEnabled(false);
|
||||||
|
ui->spinBoxPort->setEnabled(false);
|
||||||
|
|
||||||
|
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
|
||||||
|
|
||||||
|
if (isConnected()) {
|
||||||
|
|
||||||
|
Application::addToLog(
|
||||||
|
ui->textEditLog,
|
||||||
|
tr("Закрытие соединения с сервером..."));
|
||||||
|
socket.disconnectFromHost();
|
||||||
|
|
||||||
|
if (socket.state() != QAbstractSocket::UnconnectedState) {
|
||||||
|
|
||||||
|
if (!socket.waitForDisconnected(timeout)) {
|
||||||
|
|
||||||
|
Application::addToLog(
|
||||||
|
ui->textEditLog,
|
||||||
|
tr("Не удалось закрыть соединение: %1")
|
||||||
|
.arg(socket.errorString()),
|
||||||
|
Application::LogTypeError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
Application::addToLog(
|
||||||
|
ui->textEditLog, tr("Открытие соединения с сервером..."));
|
||||||
|
socket.connectToHost(
|
||||||
|
QHostAddress(ui->lineEditIp->text()),
|
||||||
|
static_cast<quint16>(ui->spinBoxPort->value())
|
||||||
|
);
|
||||||
|
|
||||||
|
if (socket.state() != QTcpSocket::SocketState::ConnectedState) {
|
||||||
|
|
||||||
|
if (!socket.waitForConnected(timeout)) {
|
||||||
|
|
||||||
|
Application::addToLog(
|
||||||
|
ui->textEditLog, tr("Не удалось открыть соединение: %1")
|
||||||
|
.arg(socket.errorString()),
|
||||||
|
Application::LogTypeError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->pushButtonConnect->setEnabled(true);
|
||||||
|
ui->lineEditData->setEnabled(true);
|
||||||
|
updateControls();
|
||||||
|
}
|
||||||
|
//==============================================================================
|
||||||
|
void MainWindow::btnSend()
|
||||||
|
{
|
||||||
|
if (!isConnected())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QStringList list = ui->lineEditData->text().split(' ', Qt::SkipEmptyParts);
|
||||||
|
QByteArray sendData;
|
||||||
|
|
||||||
|
for (const QString &str: std::as_const(list) ) {
|
||||||
|
|
||||||
|
sendData.append( static_cast<char>( str.toInt(nullptr, 16) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sendData.isEmpty()) return;
|
||||||
|
|
||||||
|
ui->pushButtonSend->setEnabled(false);
|
||||||
|
Application::addToLog(
|
||||||
|
ui->textEditLog,
|
||||||
|
tr("Отправка массива %1 байт").arg(sendData.size()));
|
||||||
|
|
||||||
|
qsizetype sendCount {0};
|
||||||
|
QElapsedTimer timer;
|
||||||
|
|
||||||
|
timer.start();
|
||||||
|
|
||||||
|
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("Ошибка отправки данных"), Application::LogTypeError );
|
||||||
|
}
|
||||||
|
else if (count > 0) {
|
||||||
|
|
||||||
|
Application::addToLog(
|
||||||
|
ui->textEditLog,
|
||||||
|
tr("Отправка данных: %1 (%2 байт)")
|
||||||
|
.arg(
|
||||||
|
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")
|
||||||
|
.arg(sendCount).arg(sendData.size()),
|
||||||
|
Application::LogTypeError );
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sendCount == 0) {
|
||||||
|
|
||||||
|
updateControls();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Application::addToLog(
|
||||||
|
ui->textEditLog,
|
||||||
|
tr("Приём массива %1 байт").arg(sendData.size()));
|
||||||
|
|
||||||
|
if (!socket.waitForReadyRead(timeout)) {
|
||||||
|
|
||||||
|
Application::addToLog(
|
||||||
|
ui->textEditLog,
|
||||||
|
tr("Таймаут ожидания приёма данных"), Application::LogTypeError );
|
||||||
|
updateControls();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray readData;
|
||||||
|
timer.restart();
|
||||||
|
|
||||||
|
while ((readData.size() < sendData.size()) && (timer.elapsed() < timeout)) {
|
||||||
|
|
||||||
|
QByteArray buf = socket.read( sendData.size() - readData.size() );
|
||||||
|
|
||||||
|
if (!buf.isEmpty()) {
|
||||||
|
|
||||||
|
Application::addToLog(
|
||||||
|
ui->textEditLog,
|
||||||
|
tr("Приём данных: %1 (%2 байт)")
|
||||||
|
.arg(
|
||||||
|
QString(buf.toHex(' '))
|
||||||
|
).arg(buf.size()),
|
||||||
|
Application::LogTypeData
|
||||||
|
);
|
||||||
|
readData.append(buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (readData.size() < sendData.size()) {
|
||||||
|
|
||||||
|
Application::addToLog(
|
||||||
|
ui->textEditLog,
|
||||||
|
tr("Таймаут приёма данных. Всего принято %1 байт из %2")
|
||||||
|
.arg(readData.size()).arg(sendData.size()),
|
||||||
|
Application::LogTypeError );
|
||||||
|
}
|
||||||
|
|
||||||
|
updateControls();
|
||||||
|
}
|
||||||
|
//==============================================================================
|
||||||
|
void MainWindow::socketConnected()
|
||||||
|
{
|
||||||
|
Application::addToLog(
|
||||||
|
ui->textEditLog,
|
||||||
|
tr("Соединение открыто. Ip: %1, Порт: %2")
|
||||||
|
.arg(ui->lineEditIp->text()).arg(ui->spinBoxPort->value()),
|
||||||
|
Application::LogTypeWarning);
|
||||||
|
updateControls();
|
||||||
|
}
|
||||||
|
//==============================================================================
|
||||||
|
void MainWindow::socketDisconnected()
|
||||||
|
{
|
||||||
|
Application::addToLog(
|
||||||
|
ui->textEditLog, tr("Соединение закрыто"), Application::LogTypeWarning);
|
||||||
|
ui->textEditLog->append("");
|
||||||
|
updateControls();
|
||||||
|
}
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
} // namespace //===============================================================
|
} // namespace //===============================================================
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#define CLIENT_MAINWINDOW_H
|
#define CLIENT_MAINWINDOW_H
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
#include <QtWidgets/QMainWindow>
|
#include <QtWidgets/QMainWindow>
|
||||||
|
#include <QtNetwork/QTcpSocket>
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
@ -47,8 +48,20 @@ public:
|
||||||
~MainWindow();
|
~MainWindow();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::MainWindow *ui;
|
const int timeout {5000};
|
||||||
|
|
||||||
|
Ui::MainWindow *ui;
|
||||||
|
QTcpSocket socket;
|
||||||
|
|
||||||
|
void initialize();
|
||||||
|
void updateControls();
|
||||||
|
bool isConnected() const;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void btnConnect();
|
||||||
|
void btnSend();
|
||||||
|
void socketConnected();
|
||||||
|
void socketDisconnected();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace //===============================================================
|
} // namespace //===============================================================
|
||||||
|
|
|
@ -24,7 +24,14 @@
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
#include "main_window.h"
|
#include "main_window.h"
|
||||||
#include "./ui_main_window.h"
|
#include "./ui_main_window.h"
|
||||||
|
|
||||||
#include <QtWidgets/QApplication>
|
#include <QtWidgets/QApplication>
|
||||||
|
#include <QtGui/QRegularExpressionValidator>
|
||||||
|
#include <QtNetwork/QHostAddress>
|
||||||
|
#include <QtCore/QDateTime>
|
||||||
|
#include <QtCore/QElapsedTimer>
|
||||||
|
|
||||||
|
#include "application_config.h"
|
||||||
|
|
||||||
namespace server { //===========================================================
|
namespace server { //===========================================================
|
||||||
|
|
||||||
|
@ -34,14 +41,257 @@ MainWindow::MainWindow(QWidget *parent)
|
||||||
, ui(new Ui::MainWindow)
|
, ui(new Ui::MainWindow)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
setWindowTitle( QApplication::applicationName() );
|
|
||||||
|
initialize();
|
||||||
|
updateControls();
|
||||||
}
|
}
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
MainWindow::~MainWindow()
|
MainWindow::~MainWindow()
|
||||||
{
|
{
|
||||||
|
disconnectClients();
|
||||||
|
server.close();
|
||||||
|
|
||||||
delete ui;
|
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 //===============================================================
|
} // mamespace //===============================================================
|
||||||
|
|
||||||
|
|
|
@ -25,8 +25,12 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#ifndef SERVER_MAINWINDOW_H
|
#ifndef SERVER_MAINWINDOW_H
|
||||||
#define SERVER_MAINWINDOW_H
|
#define SERVER_MAINWINDOW_H
|
||||||
//==============================================================================
|
|
||||||
#include <QtWidgets/QMainWindow>
|
#include <QtWidgets/QMainWindow>
|
||||||
|
#include <QtNetwork/QTcpServer>
|
||||||
|
#include <QtNetwork/QTcpSocket>
|
||||||
|
#include <QtCore/QMap>
|
||||||
|
#include <QtCore/QList>
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
@ -47,8 +51,21 @@ public:
|
||||||
~MainWindow();
|
~MainWindow();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::MainWindow *ui;
|
const qint64 timeout {5000};
|
||||||
|
|
||||||
|
Ui::MainWindow *ui;
|
||||||
|
QTcpServer server;
|
||||||
|
QList<QTcpSocket*> clients;
|
||||||
|
|
||||||
|
void initialize();
|
||||||
|
void updateControls();
|
||||||
|
void disconnectClients();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void btnConnect();
|
||||||
|
void newConnection();
|
||||||
|
void clientRead();
|
||||||
|
void clientDisconnect();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // mamespace //===============================================================
|
} // mamespace //===============================================================
|
||||||
|
|
Loading…
Reference in New Issue