Загрузка данных


#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
#include <vector>
#include <string>
#include <ctime>

#pragma comment(lib, "ws2_32.lib")
#pragma warning(disable: 4996)

using namespace std;

// ===================== НАСТРОЙКИ =====================
const int PORT = 1111;
const char* SERVER_IP = "127.0.0.1";

const int NUM_SHIPS = 2;
const int SHIP_CAPACITY = 5;

const int MIN_SAIL_TIME = 3000;
const int MAX_SAIL_TIME = 6000;

const wchar_t* CLIENT_EXE = L"ClientPilgrimage.exe";

// ===================== ГЛОБАЛЬНЫЕ ОБЪЕКТЫ =====================
HANDLE gMutex;        // защита общих данных
HANDLE gFinishEvent;  // сигнал завершения работы кораблей

// ===================== СТАТИСТИКА =====================
int gTotalPilgrims = 0;
int gByShip = 0;
int gOnFoot = 0;

// ===================== СТРУКТУРА КОРАБЛЯ =====================
struct Ship
{
    int id;
    int passengers;
    bool inPort;
    int voyages;
    HANDLE fullEvent; // сигнал: корабль заполнен или надо сделать финальный рейс
};

Ship gShips[NUM_SHIPS];

// ===================== ДАННЫЕ ПОТОКА КЛИЕНТА =====================
struct ClientContext
{
    SOCKET clientSocket;
};

// ===================== ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ =====================
int RandomRange(int left, int right)
{
    return left + rand() % (right - left + 1);
}

void SafePrint(const string& text)
{
    WaitForSingleObject(gMutex, INFINITE);
    cout << text << endl;
    ReleaseMutex(gMutex);
}

bool InitWinsock()
{
    WSADATA wsaData;
    WORD version = MAKEWORD(2, 2);

    if (WSAStartup(version, &wsaData) != 0)
    {
        cerr << "WSAStartup failed.\n";
        return false;
    }

    return true;
}

bool SendText(SOCKET s, const string& text)
{
    int result = send(s, text.c_str(), (int)text.size() + 1, 0);
    return result > 0;
}

bool RecvText(SOCKET s, string& outText)
{
    char buffer[256] = {};
    int result = recv(s, buffer, sizeof(buffer), 0);

    if (result <= 0)
        return false;

    outText = buffer;
    return true;
}

// ===================== ПОТОК КОРАБЛЯ =====================
DWORD WINAPI ShipThread(LPVOID lpParam)
{
    Ship* ship = (Ship*)lpParam;

    HANDLE waitHandles[2];
    waitHandles[0] = ship->fullEvent;
    waitHandles[1] = gFinishEvent;

    while (true)
    {
        WaitForMultipleObjects(2, waitHandles, FALSE, INFINITE);

        WaitForSingleObject(gMutex, INFINITE);

        bool finishSignaled = (WaitForSingleObject(gFinishEvent, 0) == WAIT_OBJECT_0);

        // Если завершение подано и на борту никого нет — выходим
        if (finishSignaled && ship->passengers == 0)
        {
            ReleaseMutex(gMutex);
            SafePrint("Корабль #" + to_string(ship->id) + " завершает работу.");
            return 0;
        }

        // Если почему-то проснулись, но пассажиров нет — просто продолжаем ждать
        if (ship->passengers == 0)
        {
            ResetEvent(ship->fullEvent);
            ReleaseMutex(gMutex);
            continue;
        }

        int currentPassengers = ship->passengers;
        ship->inPort = false;

        if (finishSignaled && currentPassengers < SHIP_CAPACITY)
        {
            cout << "Корабль #" << ship->id
                 << " выполняет последний рейс с "
                 << currentPassengers << " паломниками." << endl;
        }
        else
        {
            cout << "Корабль #" << ship->id
                 << " заполнен (" << currentPassengers
                 << " паломников) и отплывает." << endl;
        }

        ReleaseMutex(gMutex);

        Sleep(RandomRange(MIN_SAIL_TIME, MAX_SAIL_TIME));

        WaitForSingleObject(gMutex, INFINITE);

        ship->voyages++;
        cout << "Корабль #" << ship->id
             << " вернулся из плавания. Совершено рейсов: "
             << ship->voyages << "." << endl;

        ship->passengers = 0;
        ship->inPort = true;
        ResetEvent(ship->fullEvent);

        ReleaseMutex(gMutex);
    }
}

// ===================== ПОТОК ОБСЛУЖИВАНИЯ ОДНОГО КЛИЕНТА =====================
DWORD WINAPI ClientThread(LPVOID lpParam)
{
    ClientContext* ctx = (ClientContext*)lpParam;
    SOCKET clientSocket = ctx->clientSocket;

    string request;
    if (!RecvText(clientSocket, request))
    {
        closesocket(clientSocket);
        delete ctx;
        return 1;
    }

    int pilgrimId = 0;
    int patienceMs = 0;

    if (sscanf(request.c_str(), "%d %d", &pilgrimId, &patienceMs) != 2)
    {
        SendText(clientSocket, "ERROR");
        closesocket(clientSocket);
        delete ctx;
        return 1;
    }

    {
        WaitForSingleObject(gMutex, INFINITE);
        cout << "Паломник #" << pilgrimId
             << " прибыл в порт. Терпение: "
             << patienceMs << " мс." << endl;
        ReleaseMutex(gMutex);
    }

    DWORD startTime = GetTickCount();

    while (true)
    {
        bool boarded = false;
        int shipId = -1;
        int placeNumber = -1;

        WaitForSingleObject(gMutex, INFINITE);

        for (int i = 0; i < NUM_SHIPS; i++)
        {
            if (gShips[i].inPort && gShips[i].passengers < SHIP_CAPACITY)
            {
                gShips[i].passengers++;
                shipId = gShips[i].id;
                placeNumber = gShips[i].passengers;
                gByShip++;
                boarded = true;

                cout << "Паломник #" << pilgrimId
                     << " сел на корабль #" << shipId
                     << ". Место " << placeNumber
                     << "/" << SHIP_CAPACITY << "." << endl;

                if (gShips[i].passengers == SHIP_CAPACITY)
                {
                    SetEvent(gShips[i].fullEvent);
                }

                break;
            }
        }

        ReleaseMutex(gMutex);

        if (boarded)
        {
            string reply = "SHIP " + to_string(shipId) + " " + to_string(placeNumber);
            SendText(clientSocket, reply);

            closesocket(clientSocket);
            delete ctx;
            return 0;
        }

        DWORD waited = GetTickCount() - startTime;
        if (waited >= (DWORD)patienceMs)
        {
            WaitForSingleObject(gMutex, INFINITE);
            gOnFoot++;
            cout << "Паломник #" << pilgrimId
                 << " не дождался корабля и ушел пешком." << endl;
            ReleaseMutex(gMutex);

            SendText(clientSocket, "FOOT");
            closesocket(clientSocket);
            delete ctx;
            return 0;
        }

        Sleep(100);
    }
}

// ===================== ЗАПУСК КЛИЕНТСКИХ ПРОЦЕССОВ =====================
bool LaunchClientProcess(int pilgrimId, int patienceMs)
{
    wstring cmd = wstring(CLIENT_EXE) + L" " +
                  to_wstring(pilgrimId) + L" " +
                  to_wstring(patienceMs);

    vector<wchar_t> cmdBuffer(cmd.begin(), cmd.end());
    cmdBuffer.push_back(L'\0');

    STARTUPINFOW si;
    PROCESS_INFORMATION pi;

    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    ZeroMemory(&pi, sizeof(pi));

    BOOL ok = CreateProcessW(
        NULL,
        cmdBuffer.data(),
        NULL,
        NULL,
        FALSE,
        CREATE_NEW_CONSOLE,
        NULL,
        NULL,
        &si,
        &pi
    );

    if (!ok)
    {
        wcout << L"Не удалось запустить клиента #" << pilgrimId
              << L". Код: " << GetLastError() << endl;
        return false;
    }

    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
    return true;
}

// ===================== MAIN =====================
int main()
{
    setlocale(LC_ALL, "Russian");
    srand((unsigned int)time(nullptr));

    if (!InitWinsock())
        return 1;

    gMutex = CreateMutex(NULL, FALSE, NULL);
    if (gMutex == NULL)
    {
        cerr << "Ошибка создания mutex. Код: " << GetLastError() << endl;
        WSACleanup();
        return 1;
    }

    gFinishEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (gFinishEvent == NULL)
    {
        cerr << "Ошибка создания finish event. Код: " << GetLastError() << endl;
        CloseHandle(gMutex);
        WSACleanup();
        return 1;
    }

    for (int i = 0; i < NUM_SHIPS; i++)
    {
        gShips[i].id = i + 1;
        gShips[i].passengers = 0;
        gShips[i].inPort = true;
        gShips[i].voyages = 0;
        gShips[i].fullEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

        if (gShips[i].fullEvent == NULL)
        {
            cerr << "Ошибка создания события корабля.\n";
            CloseHandle(gFinishEvent);
            CloseHandle(gMutex);
            WSACleanup();
            return 1;
        }
    }

    HANDLE shipThreads[NUM_SHIPS];
    for (int i = 0; i < NUM_SHIPS; i++)
    {
        shipThreads[i] = CreateThread(NULL, 0, ShipThread, &gShips[i], 0, NULL);
        if (shipThreads[i] == NULL)
        {
            cerr << "Ошибка создания потока корабля.\n";
            WSACleanup();
            return 1;
        }
    }

    SOCKET listenSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (listenSocket == INVALID_SOCKET)
    {
        cerr << "Ошибка создания сокета.\n";
        return 1;
    }

    SOCKADDR_IN addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(PORT);
    addr.sin_addr.s_addr = inet_addr(SERVER_IP);

    if (bind(listenSocket, (SOCKADDR*)&addr, sizeof(addr)) == SOCKET_ERROR)
    {
        cerr << "bind failed. Код: " << WSAGetLastError() << endl;
        closesocket(listenSocket);
        return 1;
    }

    if (listen(listenSocket, SOMAXCONN) == SOCKET_ERROR)
    {
        cerr << "listen failed. Код: " << WSAGetLastError() << endl;
        closesocket(listenSocket);
        return 1;
    }

    int n;
    cout << "Введите количество паломников (1..20): ";
    cin >> n;

    while (cin.fail() || n < 1 || n > 20 || cin.peek() != '\n')
    {
        cin.clear();
        cin.ignore(32768, '\n');
        cout << "Некорректный ввод. Введите количество паломников (1..20): ";
        cin >> n;
    }

    gTotalPilgrims = n;

    cout << "Сервер запущен. Запускаю клиентские процессы...\n";

    for (int i = 0; i < n; i++)
    {
        int patience = RandomRange(2000, 7000);
        LaunchClientProcess(i + 1, patience);
        Sleep(RandomRange(200, 800));
    }

    vector<HANDLE> clientThreads;

    cout << "Ожидание подключений паломников...\n";

    for (int i = 0; i < n; i++)
    {
        SOCKET clientSocket = accept(listenSocket, NULL, NULL);
        if (clientSocket == INVALID_SOCKET)
        {
            cerr << "accept failed. Код: " << WSAGetLastError() << endl;
            continue;
        }

        ClientContext* ctx = new ClientContext;
        ctx->clientSocket = clientSocket;

        HANDLE hClientThread = CreateThread(NULL, 0, ClientThread, ctx, 0, NULL);
        if (hClientThread == NULL)
        {
            cerr << "Ошибка создания потока клиента.\n";
            closesocket(clientSocket);
            delete ctx;
            continue;
        }

        clientThreads.push_back(hClientThread);
    }

    if (!clientThreads.empty())
    {
        WaitForMultipleObjects((DWORD)clientThreads.size(), clientThreads.data(), TRUE, INFINITE);
    }

    SafePrint("Все паломники определились со способом перемещения.");

    // Будим корабли, если на них остались паломники и нужно сделать последний рейс
    WaitForSingleObject(gMutex, INFINITE);
    for (int i = 0; i < NUM_SHIPS; i++)
    {
        if (gShips[i].passengers > 0)
        {
            SetEvent(gShips[i].fullEvent);
        }
    }
    ReleaseMutex(gMutex);

    // Подаем общий сигнал завершения
    SetEvent(gFinishEvent);

    // Будим все корабли, чтобы пустые сразу завершились
    for (int i = 0; i < NUM_SHIPS; i++)
    {
        SetEvent(gShips[i].fullEvent);
    }

    WaitForMultipleObjects(NUM_SHIPS, shipThreads, TRUE, INFINITE);

    cout << "\n========== ИТОГ ==========\n";
    cout << "Всего паломников: " << gTotalPilgrims << endl;
    cout << "Добрались кораблем: " << gByShip << endl;
    cout << "Ушли пешком: " << gOnFoot << endl;
    for (int i = 0; i < NUM_SHIPS; i++)
    {
        cout << "Корабль #" << gShips[i].id
             << " совершил рейсов: " << gShips[i].voyages << endl;
    }
    cout << "==========================\n";

    for (HANDLE h : clientThreads)
        CloseHandle(h);

    for (int i = 0; i < NUM_SHIPS; i++)
    {
        CloseHandle(shipThreads[i]);
        CloseHandle(gShips[i].fullEvent);
    }

    CloseHandle(gFinishEvent);
    CloseHandle(gMutex);

    closesocket(listenSocket);
    WSACleanup();

    return 0;
}