/
Shymer123
/
Messenger
Обзор
Документация
Войти
/
Shymer123
/
Messenger
Код
Запросы
0
Задачи
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
UI_Client/SourceFiles/RequestResponseHandler/communicationHandler.cpp
800 строк
23 KB
Shymer123
The code has been refactored, now there are no incomprehensible UserRole, etc.
19 май 2025, 13:22
19 май 2025, 13:22
f8b3525
Код
Авторство
О чём код?
#include "communicationHandler.h" #include "DataModels/message_data.h" #include <QTcpSocket> #include <QJsonArray> #include <QJsonObject> #include <QJsonDocument> #include <QImage> #include <QBuffer> #include <QFile> #include <zlib.h> #include <QFileInfo> CommunicationHandler::CommunicationHandler(QTcpSocket* clientSocket) : m_clientSocket(clientSocket) { connect(m_clientSocket, &QTcpSocket::readyRead, this, &CommunicationHandler::onReadyRead); connect(m_clientSocket, &QTcpSocket::errorOccurred, this, &CommunicationHandler::onErrorOccurred); } CommunicationHandler::~CommunicationHandler() {} bool CommunicationHandler::isOnline() const { return m_clientSocket && m_clientSocket->state() == QTcpSocket::ConnectedState; } std::vector<char> CommunicationHandler::compressData(const QByteArray& inputData) { uint64_t sourceLen = inputData.size(); uint64_t destLen = compressBound(sourceLen); std::vector<char> compressedData(destLen); int result = compress(reinterpret_cast<Bytef*>(compressedData.data()), &destLen, reinterpret_cast<const Bytef*>(inputData.constData()), sourceLen); if(result != Z_OK) { qWarning() << "data compression error"; return std::vector<char>(); } compressedData.resize(destLen); return compressedData; } QByteArray CommunicationHandler::decompressData(const QByteArray& compressedData) { if (compressedData.isEmpty()) return QByteArray(); uint64_t destLen = compressedData.size() * 4; QByteArray decompressedData(destLen, '\0'); int result = ::uncompress(reinterpret_cast<Bytef*>(decompressedData.data()), &destLen, reinterpret_cast<const Bytef*>(compressedData.constData()), compressedData.size()); while(result == Z_BUF_ERROR) { qWarning() << "Buffer too small, retrying with larger buffer"; destLen *= 2; decompressedData.resize(destLen); result = ::uncompress(reinterpret_cast<Bytef*>(decompressedData.data()), &destLen, reinterpret_cast<const Bytef*>(compressedData.constData()), compressedData.size()); } if (result != Z_OK) { qWarning() << "Data decompression error, error code:" << result; return QByteArray(); } decompressedData.resize(destLen); return decompressedData; } void CommunicationHandler::getIsNameAlreadyUse(const QString& userName) { QJsonObject request; request["action"] = "isNameAlreadyUse"; request["userName"] = userName; sendRequest(request); } void CommunicationHandler::getIsPhoneNumberAlreadyUse(const QString& phoneNumber) { QJsonObject request; request["action"] = "isPhoneNumberAlreadyUse"; request["phoneNumber"] = phoneNumber; sendRequest(request); } void CommunicationHandler::addUserInDatabase(const QString& userName, const QString& hashedPassword, const QString& salt, const QString& phoneNumber) { QJsonObject request; request["action"] = "addUserInDatabase"; request["userName"] = userName; request["hashedPassword"] = hashedPassword; request["salt"] = salt; request["phoneNumber"] = phoneNumber; sendRequest(request); } void CommunicationHandler::addUserInServerCache(const int userID) { QJsonObject request; request["action"] = "addUserInCache"; request["userID"] = userID; sendRequest(request); } void CommunicationHandler::getStoredPasswordData(const QString& userName) { QJsonObject request; request["action"] = "getStoredPasswordData"; request["userName"] = userName; sendRequest(request); } void CommunicationHandler::getUserID(const QString& userName) { QJsonObject request; request["action"] = "getUserID"; request["userName"] = userName; sendRequest(request); } void CommunicationHandler::getChats(const int userID) { QJsonObject request; request["action"] = "getChats"; request["userID"] = userID; sendRequest(request); } void CommunicationHandler::getSingleChat(const int chatID) { QJsonObject request; request["action"] = "getSingleChat"; request["chatID"] = chatID; sendRequest(request); } void CommunicationHandler::getMessages(const int chatID) { QJsonObject request; request["action"] = "getMessages"; request["chatID"] = chatID; sendRequest(request); } void CommunicationHandler::sendMessage(const MessageData& data, const std::vector<int>& participantsID) { QJsonObject request; request["action"] = "sendMessage"; request["type"] = static_cast<int>(data.getMessageType()); request["userID"] = data.getUserID(); request["chatID"] = data.getChatID(); request["userName"] = data.getUserName(); request["timestamp"] = data.getTimestamp().toString("yyyy-MM-dd hh:mm:ss"); request["messageID"] = data.getMessageID(); QJsonArray participantsArr; for(const int& ID : participantsID) { participantsArr.append(ID); } request["participants"] = participantsArr; bool isFile = false; if(data.getMessageType() == MessageType::Text) { isFile = false; request["content"] = data.getContent<QString>(); } else if(data.getMessageType() == MessageType::Image) { QImage image = data.getContent<QImage>(); QByteArray byteArray; QBuffer buffer(&byteArray); buffer.open(QIODevice::WriteOnly); image.save(&buffer, "PNG"); request["content"] = QString::fromLatin1(byteArray.toBase64()); request["fileName"] = data.getAdditionalData().toString(); } else if(data.getMessageType() == MessageType::File) { isFile = true; request["comment"] = data.getAdditionalData().toString(); } if(isFile) { QList<QString> filesPaths = data.getContent<QList<QString>>(); sendFileMessage(request, filesPaths); } else { sendRequest(request); } } void CommunicationHandler::downloadFile(const int fileID, const QString& filePath, bool shouldDecompress) { QJsonObject request; request["action"] = "downloadFile"; request["fileID"] = fileID; FileTransferInfo* transfer = new FileTransferInfo(filePath); transfer->shouldDecompress = shouldDecompress; m_fileTransfers.insert(fileID, transfer); sendRequest(request); } void CommunicationHandler::getContacts(const int userID) { QJsonObject request; request["action"] = "getContacts"; request["userID"] = userID; sendRequest(request); } void CommunicationHandler::addContact(const int currentUserID, const QString& currentUserName, const int user2ID, const QString& user2Name, const QString& user2PhoneNumber) { QJsonObject request; request["action"] = "addContact"; request["currentUserID"] = currentUserID; request["currentUserName"] = currentUserName; request["user2ID"] = user2ID; request["user2Name"] = user2Name; request["user2PhoneNumber"] = user2PhoneNumber; sendRequest(request); } void CommunicationHandler::addFavourite(const int userID, const int tempChatID) { QJsonObject request; request["action"] = "addFavourite"; request["userID"] = userID; request["chatID"] = tempChatID; sendRequest(request); } void CommunicationHandler::addDialog(const int currentUserID, const QString& contactName, const int tempChatID) { QJsonObject request; request["action"] = "addDialog"; request["currentUserID"] = currentUserID; // request["user2ID"] = user2ID; request["contactName"] = contactName; request["chatID"] = tempChatID; sendRequest(request); } void CommunicationHandler::getUserData(const int chatID, const int currentUserID) { QJsonObject request; request["action"] = "getUserData"; request["chatID"] = chatID; request["currentUserID"] = currentUserID; sendRequest(request); } void CommunicationHandler::addGroup(const int userID, const QString& groupName, const QStringList& selectedMembers, const int tempChatID) { QJsonObject request; request["action"] = "addGroup"; request["userID"] = userID; request["groupName"] = groupName; request["chatID"] = tempChatID; QJsonArray membersArray; for(const QString& member : selectedMembers) { membersArray.append(member); } request["members"] = membersArray; sendRequest(request); } void CommunicationHandler::getGroupData(const int chatID) { QJsonObject request; request["action"] = "getGroupData"; request["chatID"] = chatID; sendRequest(request); } void CommunicationHandler::getMembersIDInGroup(const int userID, const int chatID) { QJsonObject request; request["action"] = "getMembersID"; request["userID"] = userID; request["chatID"] = chatID; sendRequest(request); } void CommunicationHandler::getPhoneNumber(const int userID) { QJsonObject request; request["action"] = "getPhoneNumber"; request["userID"] = userID; sendRequest(request); } void CommunicationHandler::addMembersToGroup(const int groupID, const std::vector<int>& membersID, bool isCreated, const int creatorID) { QJsonObject request; request["action"] = "addMembersToGroup"; request["groupID"] = groupID; QJsonArray membersArray; for(const int& memberID : membersID) { QJsonObject memberObject; memberObject["userID"] = memberID; memberObject["role"] = "member"; membersArray.append(memberObject); } if(!isCreated) { QJsonObject creatorObject; creatorObject["userID"] = creatorID; creatorObject["role"] = "owner"; membersArray.append(creatorObject); } request["members"] = membersArray; sendRequest(request); } void CommunicationHandler::getParticipantIDInDialogUsePhoneNumber(const QString& user2PhoneNumber) { QJsonObject request; request["action"] = "getParticipantIDInDialogUsePhoneNumber"; request["phoneNumber"] = user2PhoneNumber; sendRequest(request); } void CommunicationHandler::getIsCombinedNameExists(const int userID, const QString& combinedName) { QJsonObject request; request["action"] = "getIsCombinedNameExists"; request["userID"] = userID; request["combinedName"] = combinedName; sendRequest(request); } void CommunicationHandler::getIsPhoneNumberValid(const int userID, const QString& phoneNumber) { QJsonObject request; request["action"] = "getIsPhoneNumberValid"; request["userID"] = userID; request["phoneNumber"] = phoneNumber; sendRequest(request); } void CommunicationHandler::sendRequest(const QJsonObject& jsonRequest, bool isFile) { if(!m_clientSocket || m_clientSocket->state() != QAbstractSocket::ConnectedState) { qWarning() << "Socket is not connected"; return; } QByteArray data = QJsonDocument(jsonRequest).toJson(QJsonDocument::Compact); std::vector<char> compressedData = compressData(data); quint32 messageSize = static_cast<quint32>(compressedData.size()); QByteArray block; QDataStream out(&block, QIODevice::WriteOnly); out.setVersion(QDataStream::Qt_6_7); // 1 byte - type(0x01 - JSON, 0x02 - JSON + FILE) ; 4 byte - size out << static_cast<uint8_t>(isFile ? 0x02 : 0x01) << messageSize; block.append(compressedData.data(), compressedData.size()); int64_t totalBytesWritten = 0; int64_t blockSize = block.size(); while(totalBytesWritten < blockSize) { int64_t bytesToSend = qMin<int64_t>(8192, blockSize - totalBytesWritten); int64_t bytesWritten = m_clientSocket->write(block.constData() + totalBytesWritten, bytesToSend); if(bytesWritten == -1) { qWarning() << "Failed to write to socket:" << m_clientSocket->errorString(); return; } m_clientSocket->waitForBytesWritten(); totalBytesWritten += bytesWritten; } } void CommunicationHandler::sendFileMessage(const QJsonObject& jsonRequest, const QList<QString>& filePaths) { if(!m_clientSocket || m_clientSocket->state() != QAbstractSocket::ConnectedState) { qWarning() << "Socket is not connected"; return; } sendRequest(jsonRequest, true); const int32_t CHUNK_SIZE = 8192; QByteArray filesCount; QDataStream filesCountStream(&filesCount, QIODevice::WriteOnly); filesCountStream << static_cast<uint32_t>(filePaths.size()); m_clientSocket->write(filesCount); m_clientSocket->waitForBytesWritten(); for(const QString& filePath : filePaths) { QFile file(filePath); if(!file.open(QIODevice::ReadOnly | QIODevice::Unbuffered)) { qWarning() << "Cannot open file"; return; } QString fileName = QFileInfo(file).fileName(); QByteArray fileNameBytes = fileName.toUtf8(); uint32_t fileNameSize = fileNameBytes.size(); qint64 fileSize = file.size(); uint32_t chunkCount = static_cast<uint32_t>((fileSize + CHUNK_SIZE - 1) / CHUNK_SIZE); QByteArray fileHeader; QDataStream out(&fileHeader, QIODevice::WriteOnly); out.setVersion(QDataStream::Qt_6_7); out << fileNameSize; out.writeRawData(fileNameBytes.constData(), fileNameSize); out << static_cast<qint64>(fileSize) << chunkCount; m_clientSocket->write(fileHeader); m_clientSocket->waitForBytesWritten(); while(!file.atEnd()) { QByteArray buffer = file.read(CHUNK_SIZE); bool shouldCompress = true; if(filePath.endsWith(".zip") || filePath.endsWith(".jpg") || filePath.endsWith(".jpeg") || filePath.endsWith(".mp4")) { shouldCompress = false; } QByteArray bufferData; if(shouldCompress) { std::vector<char> compressedChunk = compressData(buffer); bufferData = QByteArray(reinterpret_cast<char*>(compressedChunk.data()), compressedChunk.size()); } else { bufferData = std::move(buffer); } QByteArray chunkData; QDataStream chunkStream(&chunkData, QIODevice::WriteOnly); chunkStream.setVersion(QDataStream::Qt_6_7); chunkStream << static_cast<uint32_t>(bufferData.size()); chunkData.append(bufferData); int64_t totalBytesWritten = 0; int64_t chunkSize = chunkData.size(); while(totalBytesWritten < chunkSize) { int64_t bytesWritten = m_clientSocket->write(chunkData.constData() + totalBytesWritten, chunkData.size() - totalBytesWritten); if(bytesWritten == -1) { qWarning() << "Failed to write to socket:" << m_clientSocket->errorString(); return; } m_clientSocket->waitForBytesWritten(); totalBytesWritten += bytesWritten; } } file.close(); } } void CommunicationHandler::onReadyRead() { m_buffer.append(m_clientSocket->readAll()); while(true) { if(static_cast<size_t>(m_buffer.size()) < sizeof(uint8_t)) return; uint8_t messageType = static_cast<uint8_t>(m_buffer[0]); if(messageType == 0x01) { if(!receiveJson()) break; } else if(messageType == 0x02) { if(!receiveFile()) break; } else { qWarning() << "Unknown type of message!"; m_buffer.clear(); return; } } } bool CommunicationHandler::receiveJson() { if(m_buffer.size() < static_cast<int>(sizeof(uint8_t) + sizeof(uint32_t))) return false; QDataStream in(&m_buffer, QIODevice::ReadOnly); in.setVersion(QDataStream::Qt_6_7); uint8_t messageType; uint32_t responseSize; in >> messageType >> responseSize; if(m_buffer.size() < static_cast<int>(sizeof(messageType) + sizeof(responseSize) + responseSize)) return false; QByteArray compressedResponse = m_buffer.mid(sizeof(messageType) + sizeof(responseSize), responseSize); m_buffer.remove(0, sizeof(messageType) + sizeof(responseSize) + responseSize); { QMutexLocker locker(&m_queueMutex); m_responseQueue.enqueue(compressedResponse); } if(!m_isProcessingResponse) { m_isProcessingResponse = true; processNextResponse(); } return true; } bool CommunicationHandler::receiveFile() { constexpr int headerSize = sizeof(uint8_t) + sizeof(uint32_t) * 2; if(m_buffer.size() < headerSize) return false; QDataStream in(&m_buffer, QIODevice::ReadOnly); in.setVersion(QDataStream::Qt_6_7); uint8_t messageType; uint32_t fileID, chunkCount; in >> messageType >> fileID >> chunkCount; if (static_cast<size_t>(m_buffer.size()) < headerSize + sizeof(uint32_t)) { qWarning() << "Not enough data to read chunkSize"; return false; } uint32_t chunkSize; in >> chunkSize; if (static_cast<size_t>(m_buffer.size()) < headerSize + sizeof(chunkSize) + chunkSize) { qWarning() << "There is not enough data in the buffer to read the chunk"; return false; } m_buffer.remove(0, headerSize + sizeof(chunkSize)); auto it = m_fileTransfers.find(fileID); if (it == m_fileTransfers.end()) { qWarning() << "Received a chunk for an unknown file"; return false; } FileTransferInfo* transfer = it.value(); if(!transfer->isDataSet) { transfer->totalChunks = chunkCount; if(!transfer->file.open(QIODevice::WriteOnly)) { qWarning() << "File opening error"; delete transfer; m_fileTransfers.erase(it); return false; } transfer->isDataSet = true; } QByteArray chunkData = m_buffer.mid(0, chunkSize); m_buffer.remove(0, chunkSize); if(transfer->shouldDecompress) { QByteArray decompressedData = decompressData(chunkData); transfer->file.write(decompressedData); } else { qDebug() << "не сжато"; transfer->file.write(chunkData); } ++transfer->receivedChunks; if (transfer->receivedChunks >= transfer->totalChunks) { transfer->file.close(); qDebug() << "File" << fileID << "fully loaded"; delete transfer; m_fileTransfers.erase(it); } return true; } void CommunicationHandler::processNextResponse() { if (m_responseQueue.isEmpty()) { m_isProcessingResponse = false; return; } QByteArray compressedResponse = m_responseQueue.dequeue(); QByteArray decompressedResponse = decompressData(compressedResponse); if (decompressedResponse.isEmpty()) { qWarning() << "Decompressed data is empty"; processNextResponse(); return; } QJsonParseError parseError; QJsonDocument doc = QJsonDocument::fromJson(decompressedResponse, &parseError); if (doc.isNull()) { qWarning() << "JSON Parse Error:" << parseError.errorString(); processNextResponse(); return; } if (!doc.isObject()) { qWarning() << "JSON is not an object."; processNextResponse(); return; } QJsonObject responseObject = doc.object(); qDebug() << "Parsed JSON Object:" << responseObject; handleResponse(responseObject); processNextResponse(); } void CommunicationHandler::handleResponse(const QJsonObject& response) { QString actionStr = response.value("action").toString(); Action action = stringToAction(actionStr); switch (action) { case Action::getIsNameAlreadyUse: emit getIsNameAlreadyUseReceived(response); break; case Action::getIsPhoneNumberAlreadyUse: emit getIsPhoneNumberAlreadyUseReceived(response); break; case Action::getStoredPasswordData: emit getStoredPasswordDataReceived(response); break; case Action::getUserID: emit getUserIDReceived(response); break; case Action::getPhoneNumber: emit getPhoneNumberReceived(response); break; case Action::getChats: emit getChatsReceived(response); break; case Action::getSingleChat: emit getSingleChatReceived(response); break; case Action::getMessages: emit getMessagesReceived(response); break; case Action::getContacts: emit getContactsReceived(response); break; case Action::getUserData: emit getUserDataReceived(response); break; case Action::getGroupData: emit getGroupDataReceived(response); break; case Action::getMembersIDInGroup: emit getMembersIDInGroupReceived(response); break; case Action::getIsCombinedNameExists: emit getIsCombinedNameExistsReceived(response); break; case Action::getIsPhoneNumberValid: emit getIsPhoneNumberValidReceived(response); break; case Action::updateChatList: emit updateChatListReceived(response); break; case Action::updateMessages: emit updateMessagesReceived(response); break; case Action::messageDelivered: emit messageDeliveredReceived(response); break; case Action::chatDelivered: emit chatDeliveredReceived(response); break; default: qWarning() << "Unknown action received: " << actionStr; break; } } void CommunicationHandler::onErrorOccurred(QAbstractSocket::SocketError socketError) { qWarning() << "Socket error: " << socketError << " - " << m_clientSocket->errorString(); } CommunicationHandler::Action CommunicationHandler::stringToAction(const QString& actionStr) { if(actionStr == "getIsNameAlreadyUse") return Action::getIsNameAlreadyUse; if(actionStr == "getIsPhoneNumberAlreadyUse") return Action::getIsPhoneNumberAlreadyUse; if(actionStr == "getStoredPasswordData") return Action::getStoredPasswordData; if(actionStr == "getUserID") return Action::getUserID; if(actionStr == "getPhoneNumber") return Action::getPhoneNumber; if(actionStr == "getChats") return Action::getChats; if(actionStr == "getSingleChat") return Action::getSingleChat; if(actionStr == "getMessages") return Action::getMessages; if(actionStr == "getContacts") return Action::getContacts; if(actionStr == "getUserData") return Action::getUserData; if(actionStr == "getParticipantIDInDialogUsePhoneNumber") return Action::getParticipantIDInDialogUsePhoneNumber; if(actionStr == "getGroupData") return Action::getGroupData; if(actionStr == "getMembersIDInGroup") return Action::getMembersIDInGroup; if(actionStr == "getIsCombinedNameExists") return Action::getIsCombinedNameExists; if(actionStr == "getIsPhoneNumberValid") return Action::getIsPhoneNumberValid; if(actionStr == "updateChatList") return Action::updateChatList; if(actionStr == "updateMessages") return Action::updateMessages; if(actionStr == "messageDelivered") return Action::messageDelivered; if(actionStr == "chatDelivered") return Action::chatDelivered; qDebug() << "Error(stringToAction)"; return Action::Unknown; }