/
Cneon90
/
xServer
Обзор
Документация
Войти
/
Cneon90
/
xServer
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
main
Server/tvz_diagnostic_tool_server.cpp
346 строк
12 KB
Кирилл
add git files
28 май 2026, 11:55
28 май 2026, 11:55
bfdd09a
Код
Авторство
О чём код?
#include "tvz_diagnostic_tool_server.h" #include "lib/CheckSum.h" TVZ_Diagnostic_Tool_Server::TVZ_Diagnostic_Tool_Server(QObject *parent) : QTcpServer(parent) , m_startTime(QDateTime::currentDateTime()) , m_totalBytesReceived(0) , m_totalBytesSent(0) { // Регистрируем все команды registerCommands(); } TVZ_Diagnostic_Tool_Server::~TVZ_Diagnostic_Tool_Server() { // Очищаем команды qDeleteAll(m_commands); m_commands.clear(); // Очищаем память при удалении сервера qDeleteAll(m_clients); m_clients.clear(); } void TVZ_Diagnostic_Tool_Server::incomingConnection(qintptr socketDescriptor) { // Создаем сокет для нового подключения QTcpSocket* clientSocket = new QTcpSocket(this); if (!clientSocket->setSocketDescriptor(socketDescriptor)) { delete clientSocket; return; } // Создаем информацию о клиенте ClientInfo* clientInfo = new ClientInfo(); clientInfo->socket = clientSocket; clientInfo->peerAddress = clientSocket->peerAddress().toString(); clientInfo->peerPort = clientSocket->peerPort(); clientInfo->connectedTime = QDateTime::currentDateTime(); clientInfo->bytesReceived = 0; clientInfo->bytesSent = 0; // Добавляем в список клиентов (потокобезопасно) { QMutexLocker locker(&m_mutex); m_clients.append(clientInfo); } // Подключаем сигналы connect(clientSocket, &QTcpSocket::readyRead, this, &TVZ_Diagnostic_Tool_Server::onReadyRead); connect(clientSocket, &QTcpSocket::bytesWritten, this, &TVZ_Diagnostic_Tool_Server::onBytesWritten); connect(clientSocket, &QTcpSocket::disconnected, this, &TVZ_Diagnostic_Tool_Server::onClientDisconnected); emit newConnection(); // Важно для QTcpServer qDebug() << "New client connected:" << clientInfo->peerAddress << ":" << clientInfo->peerPort << "-" << clientInfo->socket->IPv4Protocol; } void TVZ_Diagnostic_Tool_Server::onClientConnected() { // Этот слот вызывается при новом подключении QTcpSocket* clientSocket = qobject_cast<QTcpSocket*>(sender()); if (!clientSocket) return; // Здесь можно добавить дополнительную логику при подключении qDebug() << "Client connected (signal):" << clientSocket->peerAddress().toString() << ":" << clientSocket->peerPort(); } void TVZ_Diagnostic_Tool_Server::onReadyRead() { QTcpSocket* clientSocket = qobject_cast<QTcpSocket*>(sender()); if (!clientSocket) return; ClientInfo* clientInfo = findClient(clientSocket); if (!clientInfo) return; // Добавляем новые данные в буфер клиента clientInfo->buffer.append(clientSocket->readAll()); // Обновляем статистику клиента qint64 newBytes = clientSocket->bytesAvailable(); clientInfo->bytesReceived += newBytes; // Обновляем общую статистику (потокобезопасно) { QMutexLocker locker(&m_mutex); m_totalBytesReceived += newBytes; } // Обрабатываем все полные сообщения в буфере processClientBuffer(clientInfo); } void TVZ_Diagnostic_Tool_Server::onBytesWritten(qint64 bytes) { QTcpSocket* clientSocket = qobject_cast<QTcpSocket*>(sender()); if (!clientSocket) return; ClientInfo* clientInfo = findClient(clientSocket); if (!clientInfo) return; // Обновляем статистику клиента clientInfo->bytesSent += bytes; // Обновляем общую статистику (потокобезопасно) { QMutexLocker locker(&m_mutex); m_totalBytesSent += bytes; } } void TVZ_Diagnostic_Tool_Server::processClientBuffer(ClientInfo* client) { // Пока в буфере достаточно данных для заголовка (8 байт) while (client->buffer.size() >= 5) { // qDebug() << "NEW MESSAGE"; QDataStream stream(client->buffer); stream.setByteOrder(QDataStream::LittleEndian); quint16 cmd; quint8 param; quint32 dataSize; stream >> cmd >> param >> dataSize; // qDebug() << "CMD" << QString::number(cmd, 16); // Проверяем, достаточно ли данных для полного сообщения if (client->buffer.size() < HEADER_SIZE + dataSize) { break; // Ждем остальные данные } // Извлекаем данные сообщения QByteArray messageData = client->buffer.mid(HEADER_SIZE, dataSize); // Удаляем обработанное сообщение из буфера client->buffer.remove(0, HEADER_SIZE + dataSize); // Обрабатываем сообщение processMessage(client, cmd, param, messageData); } } void TVZ_Diagnostic_Tool_Server::processMessage(ClientInfo* client, quint16 cmd, quint8 param, const QByteArray& data) { // Ищем нужную команду в векторе CommandBase* foundCommand = nullptr; unsigned short cmdShort = static_cast<unsigned short>(cmd); for (CommandBase* command : m_commands) { if (command->Command() == cmdShort) { foundCommand = command; break; } } if (!foundCommand) { qDebug() << "Unknown command:" << QString::number(cmd, 16); // Можно отправить клиенту сообщение об ошибке sendErrorResponse(client, cmd, "Unknown command"); return; } // qDebug() << "Found command:" << foundCommand->getCommandName(); qDebug() << "DATA SIZE:" << data.size(); // Извлекаем данные тела сообщения (исключая 5 байт заголовка) // Предполагается, что заголовок состоит из: // - cmd (2 байта) // - param (1 байт) // - size (4 байта) - размер данных // const int HEADER_SIZE = 7; // 2+1+4 байта // QByteArray requestData; // if (data.size() > HEADER_SIZE) { // // Копируем только данные после заголовка // requestData = data.mid(HEADER_SIZE); // } else { // // Если данных нет или размер меньше заголовка, создаем пустой массив // qDebug() << "HUI"; // requestData = QByteArray(); // } // qDebug() << "Request data size (without header):" << requestData.size(); QByteArray message = foundCommand->response_processing(data, param); if(foundCommand->isError()) { sendErrorResponse(client, cmd, "Request processing failed"); return; } // Добавляем контрольную сумму CRC16 последними двумя байтами CheckSum checksum; uint16_t crc = checksum.calculateCRC16( reinterpret_cast<const uint8_t*>(message.constData()), static_cast<size_t>(message.size()), 0xA001); message.append(static_cast<char>(crc & 0xFF)); // младший байт message.append(static_cast<char>((crc >> 8) & 0xFF)); // старший байт (little-endian) // Отправляем qint64 sent = client->socket->write(message); if (sent > 0) { client->bytesSent += sent; { QMutexLocker locker(&m_mutex); m_totalBytesSent += sent; } } qDebug() << "Sent response to" << getClientIdentifier(client) << "Size:" << message.size() << "bytes"; } void TVZ_Diagnostic_Tool_Server::sendErrorResponse(ClientInfo* client, quint16 originalCmd, const QString& errorMessage) { QByteArray errorData; // Формируем простой ответ об ошибке // Можно сделать специальную команду для ошибок // quint16 errorCmd = 0xFFFF; // Специальная команда для ошибок // quint8 errorParam = 0x00; quint16 errorCmd = originalCmd; quint8 errorParam = 0xE0; errorData.append(reinterpret_cast<const char*>(&errorCmd), 2); errorData.append(reinterpret_cast<const char*>(&errorParam), 1); // errorData.append(errorMessage.toUtf8()); QByteArray messageData = errorMessage.toUtf8(); quint16 messageLength = static_cast<quint16>(messageData.size()); // Добавляем длину сообщения (2 байта) errorData.append(reinterpret_cast<const char*>(&messageLength), 2); // Добавляем само сообщение errorData.append(messageData); client->socket->write(errorData); } void TVZ_Diagnostic_Tool_Server::registerCommands() { m_commands.append(new cmdPong()); m_commands.append(new cmdDeviceType()); qDebug() << "Registered" << m_commands.size() << "commands:"; for (CommandBase* cmd : m_commands) { qDebug() << " -" << cmd->getCommandName() << "(ID:" << QString::number(cmd->Command(), 16) << ")"; } } void TVZ_Diagnostic_Tool_Server::onClientDisconnected() { QTcpSocket* clientSocket = qobject_cast<QTcpSocket*>(sender()); if (!clientSocket) return; ClientInfo* clientInfo = findClient(clientSocket); if (clientInfo) { qDebug() << "Client disconnected:" << clientInfo->peerAddress << ":" << clientInfo->peerPort << "Received:" << clientInfo->bytesReceived << "bytes," << "Sent:" << clientInfo->bytesSent << "bytes"; } removeClient(clientSocket); } TVZ_Diagnostic_Tool_Server::ClientInfo* TVZ_Diagnostic_Tool_Server::findClient(QTcpSocket* socket) { QMutexLocker locker(&m_mutex); for (ClientInfo* client : m_clients) { if (client->socket == socket) { return client; } } return nullptr; } void TVZ_Diagnostic_Tool_Server::removeClient(QTcpSocket* socket) { QMutexLocker locker(&m_mutex); for (int i = 0; i < m_clients.size(); ++i) { if (m_clients[i]->socket == socket) { // Удаляем сокет и информацию о клиенте m_clients[i]->socket->deleteLater(); delete m_clients[i]; m_clients.removeAt(i); break; } } } int TVZ_Diagnostic_Tool_Server::getConnectedClientsCount() const { QMutexLocker locker(&m_mutex); return m_clients.size(); } QStringList TVZ_Diagnostic_Tool_Server::getClientList() const { QStringList list; QMutexLocker locker(&m_mutex); for (const ClientInfo* client : m_clients) { QString info = QString("%1:%2 (connected: %3, received: %4 bytes, sent: %5 bytes)") .arg(client->peerAddress) .arg(client->peerPort) .arg(client->connectedTime.toString("hh:mm:ss")) .arg(client->bytesReceived) .arg(client->bytesSent); list.append(info); } return list; } qint64 TVZ_Diagnostic_Tool_Server::getTotalBytesReceived() const { QMutexLocker locker(&m_mutex); return m_totalBytesReceived; } qint64 TVZ_Diagnostic_Tool_Server::getTotalBytesSent() const { QMutexLocker locker(&m_mutex); return m_totalBytesSent; } quint64 TVZ_Diagnostic_Tool_Server::getUptimeSeconds() const { return m_startTime.secsTo(QDateTime::currentDateTime()); } QString TVZ_Diagnostic_Tool_Server::getClientIdentifier(ClientInfo* client) const { return QString("%1:%2").arg(client->peerAddress).arg(client->peerPort); }