Загрузка данных
#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;
}