/
eastopener
/
ServiceImage
Обзор
Документация
Войти
/
eastopener
/
ServiceImage
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/image_writer_service.cpp
898 строк
35 KB
Demin
Before_service
04 мар 2026, 11:45
04 мар 2026, 11:45
930a01f
Код
Авторство
О чём код?
#include "image_writer_service.h" #include <iostream> #include <csignal> #include <setupapi.h> #include <devguid.h> #pragma comment(lib, "setupapi.lib") bool AsyncServer::start(int port) { WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { std::cerr << "WSAStartup failed: " << WSAGetLastError() << std::endl; return false; } serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (serverSocket == INVALID_SOCKET) { std::cerr << "Socket creation failed: " << WSAGetLastError() << std::endl; WSACleanup(); return false; } // Устанавливаем опцию для повторного использования адреса int opt = 1; if (setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt)) == SOCKET_ERROR) { std::cerr << "Setsockopt failed: " << WSAGetLastError() << std::endl; } sockaddr_in serverAddr; serverAddr.sin_family = AF_INET; serverAddr.sin_addr.s_addr = INADDR_ANY; serverAddr.sin_port = htons(port); if (bind(serverSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) { std::cerr << "Bind failed: " << WSAGetLastError() << std::endl; closesocket(serverSocket); WSACleanup(); return false; } if (listen(serverSocket, SOMAXCONN) == SOCKET_ERROR) { std::cerr << "Listen failed: " << WSAGetLastError() << std::endl; closesocket(serverSocket); WSACleanup(); return false; } std::cout << "Server started on port " << port << std::endl; running = true; serverThread = std::thread(&AsyncServer::run, this); return true; } void AsyncServer::stop() { running = false; if (serverSocket != INVALID_SOCKET) { closesocket(serverSocket); serverSocket = INVALID_SOCKET; } if (serverThread.joinable()) { serverThread.join(); } WSACleanup(); std::cout << "Server stopped" << std::endl; } void AsyncServer::run() { std::cout << "Server is listening for connections..." << std::endl; while (running) { sockaddr_in clientAddr; int clientAddrSize = sizeof(clientAddr); SOCKET clientSocket = accept(serverSocket, (sockaddr*)&clientAddr, &clientAddrSize); if (clientSocket == INVALID_SOCKET) { if (running) { std::cerr << "Accept failed: " << WSAGetLastError() << std::endl; } continue; } // Получаем IP клиента без использования INET_ADDRSTRLEN char clientIP[16]; // Достаточно для IPv4 "255.255.255.255" const char* clientIPStr = inet_ntoa(clientAddr.sin_addr); if (clientIPStr) { strncpy(clientIP, clientIPStr, sizeof(clientIP) - 1); clientIP[sizeof(clientIP) - 1] = '\0'; } else { strcpy(clientIP, "unknown"); } std::cout << "Client connected: " << clientIP << ":" << ntohs(clientAddr.sin_port) << std::endl; // Обрабатываем клиента в отдельном потоке std::thread clientThread(&AsyncServer::handleClient, this, clientSocket); clientThread.detach(); } } void AsyncServer::handleClient(SOCKET clientSocket) { char buffer[1024]; int bytesReceived; // Устанавливаем таймаут на прием данных DWORD timeout = 30000; // 30 секунд setsockopt(clientSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)); // Получаем данные от клиента bytesReceived = recv(clientSocket, buffer, sizeof(buffer) - 1, 0); if (bytesReceived <= 0) { if (bytesReceived == SOCKET_ERROR) { int error = WSAGetLastError(); if (error == WSAETIMEDOUT) { std::cout << "Client request timeout" << std::endl; } else { std::cerr << "Receive failed: " << error << std::endl; } } closesocket(clientSocket); return; } buffer[bytesReceived] = '\0'; std::string request(buffer); std::cout << "Received request: " << request << std::endl; // Парсим запрос (формат: "диск|путь_к_образу") size_t delimiterPos = request.find('|'); if (delimiterPos == std::string::npos) { sendResponse(clientSocket, "ERROR: Invalid request format"); closesocket(clientSocket); return; } std::string drive = request.substr(0, delimiterPos); std::string imagePath = request.substr(delimiterPos + 1); // Проверяем наличие диска и файла bool driveExists = checkDriveExists(drive); bool fileExists = checkFileExists(imagePath); // Формируем базовый ответ std::string response = "DRIVE:" + std::string(driveExists ? "EXISTS" : "NOT_EXISTS") + "|FILE:" + std::string(fileExists ? "EXISTS" : "NOT_EXISTS"); // Если диск и файл существуют - записываем образ на диск if (driveExists && fileExists) { // Атомарные переменные для синхронизации std::atomic<bool> writeCompleted{false}; std::atomic<bool> clientConnected{true}; std::atomic<int> progress{0}; std::cout << "Starting disk image write: " << imagePath << " -> PhysicalDrive for " << drive << std::endl; // Запускаем поток для отправки прогресса клиенту std::thread progressThread(&AsyncServer::sendProgressUpdates, this, clientSocket, std::ref(progress), std::ref(writeCompleted), std::ref(clientConnected)); // Запускаем запись образа на диск bool writeSuccess = writeImageToDisk(drive, imagePath, progress, clientConnected); // Сообщаем потоку прогресса о завершении writeCompleted = true; progress = 100; // Ждем завершения потока прогресса if (progressThread.joinable()) { progressThread.join(); } if (writeSuccess && clientConnected) { response += "|WRITE:SUCCESS"; std::cout << "Disk image written successfully to drive " << drive << std::endl; } else if (!clientConnected) { response += "|WRITE:CANCELLED"; std::cout << "Disk write cancelled by client" << std::endl; } else { response += "|WRITE:FAILED"; std::cout << "Disk write failed" << std::endl; } } else { std::cout << "Drive or file not found. Drive: " << (driveExists ? "EXISTS" : "NOT_EXISTS") << ", File: " << (fileExists ? "EXISTS" : "NOT_EXISTS") << std::endl; } sendResponse(clientSocket, response); std::cout << "Sent response: " << response << std::endl; closesocket(clientSocket); std::cout << "Client disconnected" << std::endl; } // Конвертация const char* в const WCHAR* (выделяет память, нужно освободить) WCHAR* charToWChar(const char* str) { if (!str) return nullptr; int wlen = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0); if (wlen == 0) return nullptr; WCHAR* wstr = new WCHAR[wlen]; MultiByteToWideChar(CP_UTF8, 0, str, -1, wstr, wlen); return wstr; } // Конвертация std::string в std::wstring std::wstring stringToWstring(const std::string& str) { if (str.empty()) return L""; int wlen = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, NULL, 0); if (wlen == 0) return L""; std::wstring wstr(wlen - 1, 0); // -1 чтобы не включать null-terminator MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, &wstr[0], wlen); return wstr; } // Конвертация std::string в const WCHAR* (для временного использования) const WCHAR* stringToWCharPtr(const std::string& str) { static thread_local std::wstring wstr; // thread-local для безопасности в многопоточной среде wstr = stringToWstring(str); return wstr.c_str(); } // Безопасная конвертация с очисткой class AutoWChar { private: WCHAR* ptr; public: AutoWChar(const char* str) : ptr(charToWChar(str)) {} ~AutoWChar() { delete[] ptr; } operator const WCHAR*() const { return ptr; } bool isValid() const { return ptr != nullptr; } }; HANDLE AsyncServer::openPhysicalDrive(const std::string& drive) { if (drive.empty() || drive.length() < 1) { return INVALID_HANDLE_VALUE; } char driveLetter = toupper(drive[0]); if (driveLetter < 'A' || driveLetter > 'Z') { return INVALID_HANDLE_VALUE; } // Создаем путь к диску std::string volumePath = "\\\\.\\" + drive + ":"; // Используем автоматическую конвертацию AutoWChar wVolumePath(volumePath.c_str()); if (!wVolumePath.isValid()) { return INVALID_HANDLE_VALUE; } // Открываем диск с правами записи HANDLE hDrive = CreateFileW( wVolumePath, // Автоматическая конвертация GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, // Сначала без специальных флагов NULL ); if (hDrive == INVALID_HANDLE_VALUE) { DWORD error = GetLastError(); std::cerr << "Failed to open drive " << drive << ". Error: " << error << std::endl; // Попробуем с более ограниченными правами hDrive = CreateFileW( wVolumePath, GENERIC_READ, // Только чтение FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); } return hDrive; } bool AsyncServer::lockDrive(HANDLE hDrive) { DWORD bytesReturned; // Пытаемся заблокировать диск if (!DeviceIoControl(hDrive, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &bytesReturned, NULL)) { std::cerr << "Failed to lock volume. Error: " << GetLastError() << std::endl; return false; } // Размонтируем том if (!DeviceIoControl(hDrive, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &bytesReturned, NULL)) { std::cerr << "Failed to dismount volume. Error: " << GetLastError() << std::endl; // Продолжаем, даже если не удалось размонтировать } return true; } bool AsyncServer::unlockDrive(HANDLE hDrive) { DWORD bytesReturned; // Разблокируем диск if (!DeviceIoControl(hDrive, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0, &bytesReturned, NULL)) { std::cerr << "Failed to unlock volume. Error: " << GetLastError() << std::endl; return false; } return true; } bool AsyncServer::isDriveRemovable(const std::string& drive) { std::string rootPath = drive + ":\\"; // Используем stringToWstring для конвертации std::wstring wRootPath = stringToWstring(rootPath); if (wRootPath.empty()) { return false; } UINT driveType = GetDriveTypeW(wRootPath.c_str()); // Проверяем, съемный ли диск bool isRemovable = (driveType == DRIVE_REMOVABLE); if (!isRemovable) { std::cout << "Drive " << drive << " is not removable (type: " << driveType << ")" << std::endl; } return isRemovable; } bool AsyncServer::writeImageToDisk(const std::string& drive, const std::string& imagePath, std::atomic<int>& progress, std::atomic<bool>& clientConnected) { HANDLE hDrive = INVALID_HANDLE_VALUE; HANDLE hImage = INVALID_HANDLE_VALUE; char* buffer = nullptr; try { // 1. ВАЖНОЕ ПРЕДУПРЕЖДЕНИЕ std::cout << "Starting RAW disk write operation..." << std::endl; std::cout << "Source: " << imagePath << std::endl; std::cout << "Target: Physical drive for " << drive << std::endl; std::cout << "WARNING: All existing data on drive will be DESTROYED!" << std::endl; // 2. Проверяем, съемный ли диск if (!isDriveRemovable(drive)) { std::cerr << "Drive " << drive << " is not removable. Writing denied for safety." << std::endl; return false; } // 3. Открываем физический диск для записи hDrive = openPhysicalDrive(drive); if (hDrive == INVALID_HANDLE_VALUE) { std::cerr << "Failed to open physical drive for " << drive << ". Error: " << GetLastError() << std::endl; return false; } std::cout << "Successfully opened physical drive for " << drive << std::endl; // 4. Блокируем диск if (!lockDrive(hDrive)) { std::cerr << "Failed to lock drive " << drive << std::endl; CloseHandle(hDrive); return false; } std::cout << "Drive " << drive << " locked and dismounted successfully" << std::endl; // 5. Открываем файл образа (КОНВЕРТИРУЕМ В UNICODE) std::wstring wImagePath = stringToWstring(imagePath); if (wImagePath.empty()) { std::cerr << "Failed to convert image path to Unicode" << std::endl; return false; } hImage = CreateFileW( wImagePath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL ); if (hImage == INVALID_HANDLE_VALUE) { DWORD error = GetLastError(); std::cerr << "Failed to open image file: " << imagePath << ". Error: " << error << std::endl; // Можно попробовать альтернативный путь std::string altPath = imagePath; std::replace(altPath.begin(), altPath.end(), '/', '\\'); if (altPath != imagePath) { std::wstring wAltPath = stringToWstring(altPath); hImage = CreateFileW(wAltPath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (hImage != INVALID_HANDLE_VALUE) { std::cout << "Opened file using alternative path: " << altPath << std::endl; } } if (hImage == INVALID_HANDLE_VALUE) { unlockDrive(hDrive); CloseHandle(hDrive); return false; } } std::cout << "Image file opened successfully" << std::endl; // 6. Получаем размер файла образа LARGE_INTEGER imageSize; if (!GetFileSizeEx(hImage, &imageSize)) { std::cerr << "Failed to get image file size. Error: " << GetLastError() << std::endl; CloseHandle(hImage); unlockDrive(hDrive); CloseHandle(hDrive); return false; } std::cout << "Image file size: " << imageSize.QuadPart << " bytes" << " (" << (imageSize.QuadPart / (1024.0 * 1024.0)) << " MB)" << std::endl; // 7. Проверяем размер диска GET_LENGTH_INFORMATION lengthInfo = {0}; DWORD bytesReturned = 0; if (DeviceIoControl(hDrive, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &lengthInfo, sizeof(lengthInfo), &bytesReturned, NULL)) { std::cout << "Disk capacity: " << lengthInfo.Length.QuadPart << " bytes" << " (" << (lengthInfo.Length.QuadPart / (1024.0 * 1024.0)) << " MB)" << std::endl; if (imageSize.QuadPart > lengthInfo.Length.QuadPart) { std::cerr << "ERROR: Image is larger than disk capacity!" << std::endl; std::cerr << "Image: " << (imageSize.QuadPart / (1024.0 * 1024.0)) << " MB" << std::endl; std::cerr << "Disk: " << (lengthInfo.Length.QuadPart / (1024.0 * 1024.0)) << " MB" << std::endl; CloseHandle(hImage); unlockDrive(hDrive); CloseHandle(hDrive); return false; } } // 8. Получаем размер сектора для выравнивания DWORD bytesPerSector = 512; // По умолчанию DISK_GEOMETRY_EX diskGeometry = {0}; if (DeviceIoControl(hDrive, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0, &diskGeometry, sizeof(diskGeometry), &bytesReturned, NULL)) { bytesPerSector = diskGeometry.Geometry.BytesPerSector; std::cout << "Disk sector size: " << bytesPerSector << " bytes" << std::endl; } // 9. Используем большой выровненный буфер (4MB) const DWORD bufferSize = 4 * 1024 * 1024; // 4MB буфер DWORD alignedBufferSize = ((bufferSize + bytesPerSector - 1) / bytesPerSector) * bytesPerSector; buffer = (char*)_aligned_malloc(alignedBufferSize, bytesPerSector); if (!buffer) { std::cerr << "Failed to allocate aligned buffer" << std::endl; CloseHandle(hImage); unlockDrive(hDrive); CloseHandle(hDrive); return false; } std::cout << "Using " << (alignedBufferSize / (1024 * 1024)) << "MB aligned buffer" << std::endl; // 10. Записываем образ на диск с прогрессом LARGE_INTEGER totalWritten = {0}; DWORD bytesRead = 0; int lastReportedProgress = -1; int lastLoggedProgress = -1; std::cout << "Starting disk write operation..." << std::endl; std::cout << "Progress: 0%" << std::endl; while (totalWritten.QuadPart < imageSize.QuadPart && clientConnected) { // Читаем из файла образа if (!ReadFile(hImage, buffer, alignedBufferSize, &bytesRead, NULL)) { std::cerr << "Failed to read from image file. Error: " << GetLastError() << std::endl; _aligned_free(buffer); CloseHandle(hImage); unlockDrive(hDrive); CloseHandle(hDrive); return false; } if (bytesRead == 0) { break; // Конец файла } // Записываем на диск DWORD bytesWritten = 0; if (!WriteFile(hDrive, buffer, bytesRead, &bytesWritten, NULL)) { std::cerr << "Failed to write to disk. Error: " << GetLastError() << std::endl; _aligned_free(buffer); CloseHandle(hImage); unlockDrive(hDrive); CloseHandle(hDrive); return false; } // Проверяем, что записалось столько же, сколько прочитали if (bytesWritten != bytesRead) { std::cerr << "Write incomplete. Read " << bytesRead << " bytes, wrote " << bytesWritten << " bytes" << std::endl; _aligned_free(buffer); CloseHandle(hImage); unlockDrive(hDrive); CloseHandle(hDrive); return false; } totalWritten.QuadPart += bytesWritten; // ОБНОВЛЯЕМ ПРОГРЕСС int newProgress = static_cast<int>((totalWritten.QuadPart * 100) / imageSize.QuadPart); if (newProgress > 100) newProgress = 100; // Отправляем прогресс только если он изменился if (newProgress != lastReportedProgress) { progress.store(newProgress); lastReportedProgress = newProgress; // Логируем каждые 5% if (newProgress != lastLoggedProgress && (newProgress % 5 == 0 || newProgress == 100)) { std::cout << "Progress: " << newProgress << "%" << " (" << (totalWritten.QuadPart / (1024 * 1024)) << " MB / " << (imageSize.QuadPart / (1024 * 1024)) << " MB)" << std::endl; lastLoggedProgress = newProgress; } } } // 11. Сбрасываем кэш диска FlushFileBuffers(hDrive); // 12. Освобождаем ресурсы _aligned_free(buffer); buffer = nullptr; CloseHandle(hImage); hImage = INVALID_HANDLE_VALUE; // 13. Разблокируем диск if (!unlockDrive(hDrive)) { std::cerr << "Warning: Failed to unlock drive after write" << std::endl; } CloseHandle(hDrive); hDrive = INVALID_HANDLE_VALUE; // 14. Проверяем отмену операции if (!clientConnected) { std::cout << "Write operation cancelled by client" << std::endl; return false; } // 15. Проверяем завершение if (totalWritten.QuadPart == imageSize.QuadPart) { std::cout << "SUCCESS: Disk write completed successfully!" << std::endl; std::cout << "Total written: " << totalWritten.QuadPart << " bytes" << " (" << (totalWritten.QuadPart / (1024.0 * 1024.0)) << " MB)" << std::endl; return true; } else { std::cerr << "ERROR: Write incomplete." << std::endl; std::cerr << "Written: " << totalWritten.QuadPart << " of " << imageSize.QuadPart << " bytes" << std::endl; std::cerr << "Remaining: " << (imageSize.QuadPart - totalWritten.QuadPart) << " bytes" << std::endl; return false; } } catch (const std::exception& e) { std::cerr << "EXCEPTION during disk write: " << e.what() << std::endl; // Очистка ресурсов в случае исключения if (buffer) { _aligned_free(buffer); } if (hImage != INVALID_HANDLE_VALUE) { CloseHandle(hImage); } if (hDrive != INVALID_HANDLE_VALUE) { unlockDrive(hDrive); CloseHandle(hDrive); } return false; } } bool AsyncServer::checkDriveExists(const std::string& drive) { if (drive.empty()) return false; DWORD drives = GetLogicalDrives(); char driveLetter = toupper(drive[0]); if (driveLetter >= 'A' && driveLetter <= 'Z') { int bitPosition = driveLetter - 'A'; return (drives & (1 << bitPosition)) != 0; } return false; } bool AsyncServer::checkFileExists(const std::string& fullPath) { if (fullPath.empty()) { return false; } // Используем stringToWstring std::wstring wFullPath = stringToWstring(fullPath); if (wFullPath.empty()) { return false; } DWORD attributes = GetFileAttributesW(wFullPath.c_str()); if (attributes == INVALID_FILE_ATTRIBUTES) { DWORD error = GetLastError(); if (error != ERROR_FILE_NOT_FOUND && error != ERROR_PATH_NOT_FOUND) { std::cerr << "GetFileAttributesW failed for " << fullPath << ". Error: " << error << std::endl; } return false; } // Проверяем, что это файл, а не директория bool isFile = !(attributes & FILE_ATTRIBUTE_DIRECTORY); if (isFile) { // Дополнительная проверка размера файла AutoWChar wPath(fullPath.c_str()); if (wPath.isValid()) { HANDLE hFile = CreateFileW(wPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile != INVALID_HANDLE_VALUE) { LARGE_INTEGER fileSize; if (GetFileSizeEx(hFile, &fileSize)) { std::cout << "File " << fullPath << " exists, size: " << fileSize.QuadPart << " bytes" << std::endl; } CloseHandle(hFile); } } } return isFile; } // bool AsyncServer::createFileCopyWithNewName(const std::string& drive, const std::string& originalFilePath, // std::atomic<int>& progress, std::string& newFilePath, // std::atomic<bool>& clientConnected) { // try { // // Получаем путь к директории и имя файла // std::filesystem::path originalPath(originalFilePath); // std::string directory = originalPath.parent_path().string(); // std::string filename = originalPath.filename().string(); // // Создаём новое имя файла: "F_proshivka.iso" // // newFilePath = directory + "\\" + drive + "_" + filename; // newFilePath = drive + ":\\" + filename; // // Открываем файлы // std::ifstream sourceFile(originalFilePath, std::ios::binary); // std::ofstream destFile(newFilePath, std::ios::binary); // if (!sourceFile.is_open()) { // std::cerr << "Failed to open source file: " << originalFilePath << std::endl; // return false; // } // if (!destFile.is_open()) { // std::cerr << "Failed to create destination file: " << newFilePath << std::endl; // return false; // } // // Получаем размер файла для прогресса // sourceFile.seekg(0, std::ios::end); // std::streamsize fileSize = sourceFile.tellg(); // sourceFile.seekg(0, std::ios::beg); // if (fileSize <= 0) { // std::cerr << "Invalid file size: " << fileSize << std::endl; // return false; // } // std::cout << "File size: " << fileSize << " bytes" << std::endl; // // Увеличиваем размер буфера для ускорения копирования // const size_t bufferSize = 1024 * 1024 * 4; // 4MB // std::vector<char> buffer(bufferSize); // std::streamsize totalCopied = 0; // int lastReportedProgress = -1; // // Копируем файл с проверкой соединения // while (sourceFile && clientConnected) { // sourceFile.read(buffer.data(), bufferSize); // std::streamsize bytesRead = sourceFile.gcount(); // if (bytesRead > 0) { // destFile.write(buffer.data(), bytesRead); // if (destFile.fail()) { // std::cerr << "Error writing to destination file" << std::endl; // return false; // } // totalCopied += bytesRead; // // Обновляем прогресс (0-100%) // int newProgress = static_cast<int>((totalCopied * 100) / fileSize); // if (newProgress > 100) newProgress = 100; // if (newProgress != lastReportedProgress) { // progress.store(newProgress); // lastReportedProgress = newProgress; // if (newProgress % 10 == 0) { // Логируем каждые 10% // std::cout << "Copy progress: " << newProgress << "%" << std::endl; // } // } // } // } // // Проверяем, была ли операция отменена // if (!clientConnected) { // std::cout << "Copy operation cancelled by client" << std::endl; // sourceFile.close(); // destFile.close(); // return false; // } // // Проверяем ошибки чтения // if (sourceFile.fail() && !sourceFile.eof()) { // std::cerr << "Error reading source file" << std::endl; // return false; // } // sourceFile.close(); // destFile.close(); // // Проверяем размер скопированного файла // std::filesystem::path copiedPath(newFilePath); // if (std::filesystem::file_size(copiedPath) != static_cast<uintmax_t>(fileSize)) { // std::cerr << "Copied file size doesn't match original" << std::endl; // return false; // } // std::cout << "File copy completed successfully" << std::endl; // return true; // } catch (const std::exception& e) { // std::cerr << "Error copying file: " << e.what() << std::endl; // return false; // } // } void AsyncServer::sendProgressUpdates(SOCKET clientSocket, std::atomic<int>& progress, std::atomic<bool>& completed, std::atomic<bool>& clientConnected) { int lastProgress = -1; // Устанавливаем неблокирующий режим для этого сокета u_long mode = 1; // Неблокирующий режим ioctlsocket(clientSocket, FIONBIO, &mode); while (!completed && clientConnected) { int currentProgress = progress.load(std::memory_order_relaxed); // Отправляем прогресс только если он изменился if (currentProgress != lastProgress) { std::string progressMessage = "PROGRESS:" + std::to_string(currentProgress) + "\n"; // Пытаемся отправить прогресс клиенту int sendResult = send(clientSocket, progressMessage.c_str(), progressMessage.length(), 0); if (sendResult == SOCKET_ERROR) { int error = WSAGetLastError(); if (error != WSAEWOULDBLOCK) { // Не только "буфер полон", а реальная ошибка std::cout << "Client disconnected during file copy. Error: " << error << std::endl; clientConnected = false; break; } // WSAEWOULDBLOCK - нормально для неблокирующего сокета, продолжаем } lastProgress = currentProgress; } // ПРОВЕРКА СОЕДИНЕНИЯ: используем select с очень коротким таймаутом fd_set readfds; FD_ZERO(&readfds); FD_SET(clientSocket, &readfds); struct timeval tv; tv.tv_sec = 0; tv.tv_usec = 1000; // Только 1ms, не блокируем надолго int selectResult = select(0, &readfds, nullptr, nullptr, &tv); if (selectResult == SOCKET_ERROR) { // Ошибка select clientConnected = false; break; } else if (selectResult > 0) { // Есть данные для чтения (возможно FIN) char buffer[1]; int bytes = recv(clientSocket, buffer, sizeof(buffer), MSG_PEEK); if (bytes == 0) { // Клиент закрыл соединение std::cout << "Client gracefully closed connection" << std::endl; clientConnected = false; break; } else if (bytes == SOCKET_ERROR) { int err = WSAGetLastError(); if (err != WSAEWOULDBLOCK) { // Реальная ошибка std::cout << "Socket error in progress thread: " << err << std::endl; clientConnected = false; break; } } } // Небольшая задержка между обновлениями std::this_thread::sleep_for(std::chrono::milliseconds(10)); } // Восстанавливаем блокирующий режим (если нужно) mode = 0; ioctlsocket(clientSocket, FIONBIO, &mode); } void AsyncServer::sendResponse(SOCKET clientSocket, const std::string& response) { send(clientSocket, response.c_str(), response.length(), 0); } std::atomic<bool> shutdownRequested{false}; void signalHandler(int signal) { if (signal == SIGINT) { std::cout << "\nReceived shutdown signal..." << std::endl; shutdownRequested = true; } } int main() { // Устанавливаем обработчик сигналов для graceful shutdown std::signal(SIGINT, signalHandler); AsyncServer server; std::cout << "Starting Async File Server..." << std::endl; std::cout << "Server will run on port 8080" << std::endl; std::cout << "Press Ctrl+C to stop the server" << std::endl; if (!server.start(8080)) { std::cerr << "Failed to start server" << std::endl; return 1; } // Ожидаем сигнал завершения while (!shutdownRequested) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); } std::cout << "Shutting down server..." << std::endl; server.stop(); std::cout << "Server stopped successfully" << std::endl; return 0; }