Compare commits

..

9 Commits

Author SHA1 Message Date
2d0bd162d6 Breaking update 2025-05-05 15:31:08 +03:00
896f1bb951 Little fix 2025-05-04 22:37:11 +03:00
b0fb83e030 Make vector diagram + comments for DFS method 2025-05-04 22:25:57 +03:00
651e8ec5aa Add diagrams and comments 2025-05-04 14:17:17 +03:00
725777d4fa Merge pull request 'Almost ready project' (#2) from Refactor into main
Reviewed-on: https://git.tjoyspotifylastfm.tech/ParkSuMin/Maze/pulls/2
2025-05-01 00:15:02 +02:00
12d5ca5d41 Gracefull down of c++ standart version
Add stress-test shell script
2025-05-01 02:36:10 +03:00
787164f086 Little cosmetic fix 2025-05-01 02:06:51 +03:00
bec5293659 Move iostream to hpp part
Fix visibility of maze's flag
2025-05-01 01:36:10 +03:00
d6966f69db Doxygen config 2025-05-01 01:27:21 +03:00
15 changed files with 2751 additions and 38 deletions

3
.gitignore vendored
View File

@@ -3,4 +3,5 @@ CMakeFiles/
cmake* cmake*
CMakeC* CMakeC*
Makefile Makefile
maze_* maze_*
documentation/

View File

@@ -1,9 +1,12 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.5)
project(maze LANGUAGES CXX) project(maze LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++23 -Wall") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20 -Wall")
add_compile_options(-fsanitize=address)
add_link_options(-fsanitize=address)
include_directories( include_directories(
include include

2496
Doxyfile Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -12,7 +12,7 @@
Клиент должен работать по разработанному сетевому протоколу. Клиент должен работать по разработанному сетевому протоколу.
## Разработанное приложение ## Разработанное приложение
Реализация [сервера на основе многопоточности](doc/multithread_maze_server.md) и клиента эхо-приложения. Реализация [сервера на основе многопоточности](doc/multithread_maze_server.md) и клиента игры-приложения.
## Требования ## Требования
Для сборки и запуска необходима Unix-система и компилятор C++ с поддержкой стандарта c++23. Для сборки и запуска необходима Unix-система и компилятор C++ с поддержкой стандарта c++23.
@@ -29,3 +29,9 @@ cmake ..
make make
``` ```
Собранные таким способом приложения сервера и клиента будут находиться в папке `build`. Собранные таким способом приложения сервера и клиента будут находиться в папке `build`.
## (Опционально) Генерация документации
Для генерации документации необходимо установить [Doxygen](https://www.doxygen.nl/download.html) и выполнить команду:
```bash
doxygen Doxyfile
```

25
doc/maze.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

@@ -3,14 +3,15 @@
## Протокол пользовательского уровня ## Протокол пользовательского уровня
UML-диаграмма протокола пользовательского уровня представлена на рисунке ниже. UML-диаграмма протокола пользовательского уровня представлена на рисунке ниже.
![UML-диаграмма протокола пользовательского уровня](echo-protocol.svg)
![UML-диаграмма протокола пользовательского уровня](maze.svg)
## Описание работы приложения ## Описание работы приложения
Сервер `maze-server` ожидает подключения клиентов по протоколу TCP на заданный порт (по умолчанию 1024). После установления соединения сервер запрашивает имя игрока, а затем начинает игру в лабиринте. Лабиринт генерируется случайным образом при старте каждой новой игровой сессии. Гарантируется наличие хотя бы одного пути из начальной точки `(0, 0)` в конечную точку `(2, 2)`. Добавление дополнительных стен происходит с соблюдением условия достижимости цели. Сервер `MazeServer` ожидает подключения клиентов по протоколу TCP на заданный порт (по умолчанию 2000). После установления соединения сервер запрашивает имя игрока, а затем начинает игру в лабиринте. Лабиринт генерируется случайным образом при старте каждой новой игровой сессии. Гарантируется наличие хотя бы одного пути из начальной точки `(0, 0)` в конечную точку `(2, 2)`. Добавление дополнительных стен происходит с соблюдением условия достижимости цели.
Cхема и нумерация клеток лабиринта представлена на рисунке ниже. Cхема и нумерация клеток лабиринта представлена на рисунке ниже.
![Схема лабиринта](maze_numeration.svg) ![Схема лабиринта](numeration_maze.svg)
Клиент отправляет команды движения ("вперёд", "направо", "налево", "назад") или команду "сдаюсь" для завершения игры. Сервер обрабатывает команды, проверяет возможность хода, обновляет состояние лабиринта и отправляет клиенту текстовый ответ с результатом хода, количеством оставшихся ходов и текущей позицией (в формате координат). Игра завершается, если игрок достигает конечной точки (позиция 8), исчерпывает ходы или сдаётся. Клиент отправляет команды движения ("вперёд", "направо", "налево", "назад") или команду "сдаюсь" для завершения игры. Сервер обрабатывает команды, проверяет возможность хода, обновляет состояние лабиринта и отправляет клиенту текстовый ответ с результатом хода, количеством оставшихся ходов и текущей позицией (в формате координат). Игра завершается, если игрок достигает конечной точки (позиция 8), исчерпывает ходы или сдаётся.
@@ -24,10 +25,11 @@ Cхема и нумерация клеток лабиринта представ
- `-s` — включает сервисный режим (без ограничений по количеству шагов). - `-s` — включает сервисный режим (без ограничений по количеству шагов).
```bash ```bash
./maze-server -h localhost -p 1024 -n 10 -s ./maze-server -h localhost -p 2000 -n 10 -s
``` ```
По умолчанию сокет сервера связывается с адресом `localhost:1024`, игрокам разрешено 10 шагов, сервисный режим выключен. По умолчанию сокет сервера связывается с адресом `localhost:2000`, игрокам разрешено 10 шагов, сервисный режим выключен.
Завершения работы сервера — `Ctrl+C`.
Завершение работы сервера — `Ctrl+C`.
Клиент подключается к серверу, отправляет своё имя и получает интерактивную игру в лабиринт. Клиент подключается к серверу, отправляет своё имя и получает интерактивную игру в лабиринт.
Поддерживаемые команды: Поддерживаемые команды:
@@ -46,7 +48,7 @@ Cхема и нумерация клеток лабиринта представ
- `-p` — задаёт номер порта сервера. - `-p` — задаёт номер порта сервера.
```bash ```bash
./maze_client -h localhost -p 1024 ./maze_client -h localhost -p 2000
``` ```
По умолчанию клиент пытается подключиться к адресу `localhost:1024`. По умолчанию клиент пытается подключиться к адресу `localhost:2000`.

View File

@@ -1,3 +1,8 @@
/*! @file client.hpp
Заголовочный файл клиента для игры в лабиринт на базе сокетов.
@author ParkSuMin
@date 2025.04.30 */
#ifndef CLIENT_HPP #ifndef CLIENT_HPP
#define CLIENT_HPP #define CLIENT_HPP
@@ -8,15 +13,34 @@
#include <unistd.h> #include <unistd.h>
#include <netdb.h> #include <netdb.h>
/*! Размер буфера для обмена данными между сервером и клиентом. */
const int BUFFER_SIZE = 1024;
/*! Класс клиента для взаимодействия с сервером игры в лабиринт. */
class Client{ class Client{
private: private:
int sock; int sock; ///< Сокет для соединения с сервером.
public: public:
/*! Конструктор по умолчанию.
@details Конструктор создаёт сокет клиента и связывает его с предоставленным операционной системой адресом. В
случае ошибки вызывает исключение.
@throw std::runtime_error При ошибке создания или связывания сокета. */
Client(); Client();
void run(const std::string&, const unsigned short); /*! Запускает клиентское приложение.
@details Устанавливает соединение с сервером по указанному хосту и порту, после чего вызывает игровой цикл.
@param host Адрес сервера (название хоста или IP-адрес).
@param port Порт сервера для подключения. */
void run(const std::string& host, const unsigned short port);
/*! Основной игровой цикл.
@details Обрабатывает ввод пользователя, отправляет команды на сервер и получает ответы. */
void game(); void game();
/*! Отправляет тестовый запрос (ping) на сервер.
@details Используется для проверки доступности сервера.
@param server_address Структура адреса сервера.
@return Целое число, указывающее результат выполнения (0 — успех, иначе — ошибка). */
int ping(struct sockaddr_in); int ping(struct sockaddr_in);
}; };

View File

@@ -1,35 +1,69 @@
/*! @file maze.hpp
Заголовочный файл класса лабиринта для игры.
@author ParkSuMin
@date 2025.04.30 */
#ifndef MAZE_HPP #ifndef MAZE_HPP
#define MAZE_HPP #define MAZE_HPP
#include <vector> #include <vector>
#include <algorithm>
#include <unordered_map> #include <unordered_map>
#include <ctime> #include <ctime>
#include <climits> #include <climits>
#include <random> #include <random>
#include <iostream>
/*! Размер лабиринта (количество узлов). */
const int MAZE_SIZE = 9; const int MAZE_SIZE = 9;
/*! Количество направлений движения (вперёд, направо, назад, налево). */
const int DIRECTIONS = 4; const int DIRECTIONS = 4;
/*! Минимальное количество стен в лабиринте. */
const int MIN_WALLS = 3; const int MIN_WALLS = 3;
/*! Максимальное количество стен в лабиринте. */
const int MAX_WALLS = 5; const int MAX_WALLS = 5;
/*! Класс лабиринта для игры. */
class Maze{ class Maze{
private: private:
std::unordered_map<int, std::vector<bool>> graph; std::unordered_map<int, std::vector<bool>> graph; ///< Граф лабиринта, где узлы связаны направлениями.
int moves_left; int moves_left; ///< Количество оставшихся ходов.
/*! Проверяет наличие пути между двумя узлами.
@details Использует поиск в ширину (BFS) для проверки существования пути от start до end.
@param start Начальный узел.
@param end Конечный узел.
@return true, если путь существует, false — иначе. */
bool is_path_exists(int start, int end); bool is_path_exists(int start, int end);
/*! Структура, представляющая ребро в графе лабиринта. */
struct Edge { struct Edge {
int node1, dir1; int node1; ///< Первый узел ребра.
int node2, dir2; int dir1; ///< Направление от первого узла.
int node2; ///< Второй узел ребра.
int dir2; ///< Направление от второго узла.
}; };
public: public:
bool test_mode; bool test_mode; ///< Флаг тестового режима (без ограничения ходов).
/*! Создаёт лабиринт.
@details Инициализирует граф лабиринта, добавляет случайные стены и задаёт количество ходов.
@param flag Флаг тестового режима (true — без ограничения ходов).
@param steps Количество ходов, доступных игроку (игнорируется в тестовом режиме). */
Maze(bool flag, int steps); Maze(bool flag, int steps);
/*! Возвращает количество оставшихся ходов.
@return Целое число, представляющее оставшиеся ходы. */
int get_moves_left() const; int get_moves_left() const;
/*! Устанавливает количество оставшихся ходов.
@param _steps Новое значение количества ходов. */
void set_moves_left(int _steps); void set_moves_left(int _steps);
/*! Проверяет наличие стены в указанном направлении от узла.
@param node Узел лабиринта.
@param direction Направление (0 — вперёд, 1 — направо, 2 — назад, 3 — налево).
@return true, если в указанном направлении стена, false — иначе. */
bool is_wall(int node, int direction) const; bool is_wall(int node, int direction) const;
}; };
#endif #endif

View File

@@ -1,3 +1,8 @@
/*! @file server.hpp
Заголовочный файл сервера для игры в лабиринт на базе сокетов.
@author ParkSuMin
@date 2025.04.30 */
#ifndef SERVER_HPP #ifndef SERVER_HPP
#define SERVER_HPP #define SERVER_HPP
@@ -11,18 +16,43 @@
#include <unistd.h> #include <unistd.h>
#include <cstring> #include <cstring>
/*! Максимальное количество клиентов, которые могут быть в очереди на подключение. */
const int MAX_CLIENTS = 512; const int MAX_CLIENTS = 512;
/*! Размер буфера для обмена данными между сервером и клиентом. */
const int BUFFER_SIZE = 1024; const int BUFFER_SIZE = 1024;
/*! Класс сервера для игры в лабиринт с использованием сокетов (протокол TCP). */
class Server { class Server {
private: private:
bool service_mode; bool service_mode; ///< Флаг сервисного режима (тестовый режим без ограничения ходов).
int server_socket; int server_socket; ///< Серверный сокет для обработки клиентских подключений.
/*! Обрабатывает подключение одного клиента.
@details Создаёт лабиринт для клиента, принимает команды, обновляет состояние игры и отправляет ответы.
@param socket Сокет клиента.
@param flag Флаг сервисного режима.
@param steps Количество ходов, доступных игроку (игнорируется в сервисном режиме). */
void handle_client(int socket, bool flag, int steps); void handle_client(int socket, bool flag, int steps);
/*! Проверяет статус игры.
@details Проверяет, остались ли ходы у игрока, и активен ли тестовый режим.
@param maze Экземпляр объекта класса лабиринта.
@return true, если игра продолжается, false — если игра завершена. */
bool check_status(Maze& maze); bool check_status(Maze& maze);
public: public:
/*! Создаёт экземпляр сервера.
@details Инициализирует серверный сокет, связывает его с указанным хостом и портом.
@throw std::runtime_error При ошибке создания сокета, получения хоста или привязки.
@param host Адрес сервера (название хоста или IP-адрес).
@param port Порт сервера для подключения клиентов. */
Server(const std::string& host, const unsigned short port); Server(const std::string& host, const unsigned short port);
/*! Запускает сервер.
@details Начинает прослушивание входящих подключений и создаёт отдельные потоки для обработки клиентов.
@throw std::runtime_error При ошибке прослушивания или принятия соединения.
@param steps Количество ходов, доступных каждому игроку.
@param service_flag Флаг активации сервисного режима. */
void start(int steps, bool service_flag); void start(int steps, bool service_flag);
}; };

12
server-test.sh Executable file
View File

@@ -0,0 +1,12 @@
#!/bin/bash
if [[ $# -lt 1 ]]; then
echo "no client path!"
exit 1
fi
COUNTER=0
while [[ COUNTER -lt 500 ]]; do
./$1 < Doxyfile > /dev/null &
let COUNTER++
done
echo "finish"

View File

@@ -1,3 +1,8 @@
/*! @file client.cpp
Исходный файл клиента для игры в лабиринт на базе сокетов.
@author ParkSuMin
@date 2025.04.30 */
#include "client.hpp" #include "client.hpp"
// void print_instructions() { // void print_instructions() {
@@ -44,6 +49,10 @@ void Client::run(const std::string& h, const unsigned short p) {
std::string player_name; std::string player_name;
std::getline(std::cin, player_name); std::getline(std::cin, player_name);
if (player_name.empty() || player_name.size() > BUFFER_SIZE) {
throw std::runtime_error("Incorrect name!");
}
if (!ping(serv_addr)) { if (!ping(serv_addr)) {
throw std::runtime_error("Connection lost!"); throw std::runtime_error("Connection lost!");
} }
@@ -56,25 +65,30 @@ void Client::game(){
std::cout << "Игра началась!" << std::endl; std::cout << "Игра началась!" << std::endl;
//print_instructions(); //print_instructions();
char buffer[1024] = {0}; char buffer[BUFFER_SIZE] = {0};
while (true) { while (true) {
std::cout << "Введите команду: "; std::cout << "Введите команду: ";
std::string command; std::string command;
std::getline(std::cin, command); std::getline(std::cin, command);
if (command.empty() || command.size() > BUFFER_SIZE) {
std::cout << "Некорректная команда" << std::endl;
continue;
}
// Отправка команды на сервер // Отправка команды на сервер
send(sock, command.c_str(), command.size(), 0); send(sock, command.c_str(), command.size(), 0);
// Получение ответа от сервера // Получение ответа от сервера
memset(buffer, 0, sizeof(buffer)); memset(buffer, 0, sizeof(buffer));
int bytes_received = recv(sock, buffer, sizeof(buffer), 0); int bytes_received = recv(sock, buffer, sizeof(buffer), 0);
if (bytes_received <= 0) { if (bytes_received == 0 || bytes_received > BUFFER_SIZE) {
throw std::runtime_error("Connection lost!"); throw std::runtime_error("Connection lost!");
//break; //break;
} }
std::string response(buffer); std::string response(buffer);
std::cout << "Ответ сервера: " << response; std::cout << "Ответ сервера: " << response << std::endl;
// Проверка завершения игры // Проверка завершения игры
if (response.find("вы выиграли") != std::string::npos || if (response.find("вы выиграли") != std::string::npos ||

View File

@@ -1,9 +1,34 @@
/*! @file client_main.cpp
Главный файл клиента для игры в лабиринт на базе сокетов.
@author ParkSuMin
@date 2025.04.30 */
#include "client.hpp" #include "client.hpp"
/*! Главная функция клиента.
@details Обрабатывает аргументы командной строки, создаёт экземпляр клиента и запускает его.
@throw std::runtime_error При ошибке работы клиента.
@throw std::exception При непредвиденной ошибке.
@dot
digraph main {
ranksep=0.25;
node [shape=box,fontsize="10",fixedsize=true,width=2,height=0.3]
edge [arrowsize=0.5]
Beg [label="Начало",shape=ellipse]
End [label="Конец",shape=ellipse]
A [label="Проверка аргументов"]
B [label="Создание клиента"]
C [label="Запуск клиента"]
D [label="Обработка исключений"]
Beg->A->B->C->End
B->D
C->D->End
}
@enddot */
int main(int argc, char** argv){ int main(int argc, char** argv){
int opt; int opt;
std::string host = "localhost"; std::string host = "localhost";
short unsigned port = 1024u; short unsigned port = 2000u;
while ((opt = getopt(argc, argv, "h:p:")) != -1) { while ((opt = getopt(argc, argv, "h:p:")) != -1) {
switch (opt) { switch (opt) {

View File

@@ -1,8 +1,14 @@
/*! @file maze.cpp
Исходный файл класса лабиринта для игры.
@author ParkSuMin
@date 2025.04.30 */
#include "maze.hpp" #include "maze.hpp"
#include <iostream>
Maze::Maze(bool _test_mode, int _steps){ Maze::Maze(bool _test_mode, int _steps){
std::vector<Edge> edges = { // Инициализация графа лабиринта и его ребер
// Индексы направлений: 0 - север, 1 - восток, 2 - юг, 3 - запад
std::vector<Edge> edges = {
{0, 1, 1, 3}, {0, 0, 3, 2}, {1, 1, 2, 3}, {1, 0, 4, 2}, {0, 1, 1, 3}, {0, 0, 3, 2}, {1, 1, 2, 3}, {1, 0, 4, 2},
{2, 0, 5, 2}, {3, 1, 4, 3}, {3, 0, 6, 2}, {4, 1, 5, 3}, {2, 0, 5, 2}, {3, 1, 4, 3}, {3, 0, 6, 2}, {4, 1, 5, 3},
{4, 0, 7, 2}, {5, 0, 8, 2}, {6, 1, 7, 3}, {7, 1, 8, 3} {4, 0, 7, 2}, {5, 0, 8, 2}, {6, 1, 7, 3}, {7, 1, 8, 3}
@@ -14,11 +20,14 @@ Maze::Maze(bool _test_mode, int _steps){
graph[i] = std::vector<bool>(DIRECTIONS, true); graph[i] = std::vector<bool>(DIRECTIONS, true);
} }
// Выставление внешних стен
graph[6][0] = graph[7][0] = graph[8][0] = false; graph[6][0] = graph[7][0] = graph[8][0] = false;
graph[0][2] = graph[1][2] = graph[2][2] = false; graph[0][2] = graph[1][2] = graph[2][2] = false;
graph[0][3] = graph[3][3] = graph[6][3] = false; graph[0][3] = graph[3][3] = graph[6][3] = false;
graph[2][1] = graph[5][1] = graph[8][1] = false; graph[2][1] = graph[5][1] = graph[8][1] = false;
// Добавление внутренних стен
std::mt19937 rng(time(nullptr)); std::mt19937 rng(time(nullptr));
std::shuffle(edges.begin(), edges.end(), rng); std::shuffle(edges.begin(), edges.end(), rng);
int walls_to_add = std::uniform_int_distribution<int>(MIN_WALLS, MAX_WALLS)(rng); int walls_to_add = std::uniform_int_distribution<int>(MIN_WALLS, MAX_WALLS)(rng);
@@ -27,6 +36,7 @@ Maze::Maze(bool _test_mode, int _steps){
const auto& edge = edges[i]; const auto& edge = edges[i];
graph[edge.node1][edge.dir1] = false; graph[edge.node1][edge.dir1] = false;
graph[edge.node2][edge.dir2] = false; graph[edge.node2][edge.dir2] = false;
// Проверка на существование пути
if (!is_path_exists(0, 8)) { if (!is_path_exists(0, 8)) {
graph[edge.node1][edge.dir1] = true; graph[edge.node1][edge.dir1] = true;
graph[edge.node2][edge.dir2] = true; graph[edge.node2][edge.dir2] = true;

View File

@@ -1,3 +1,8 @@
/*! @file server.cpp
Исходный файл сервера для игры в лабиринт на базе сокетов.
@author ParkSuMin
@date 2025.04.30 */
#include "server.hpp" #include "server.hpp"
bool Server::check_status(Maze &maze) { bool Server::check_status(Maze &maze) {
@@ -36,8 +41,8 @@ void Server::handle_client(int client_socket, bool mode, int steps) {
std::string response; std::string response;
int bytes_received = recv(client_socket, buffer, sizeof(buffer), 0); int bytes_received = recv(client_socket, buffer, sizeof(buffer), 0);
if (bytes_received <= 0) { if (bytes_received <= 0 || bytes_received > BUFFER_SIZE) {
std::cout << "Error in getting player name"; std::cout << "Error in getting player name" << std::endl;;
close(client_socket); close(client_socket);
return; return;
} }
@@ -50,7 +55,7 @@ void Server::handle_client(int client_socket, bool mode, int steps) {
memset(buffer, 0, sizeof(buffer)); memset(buffer, 0, sizeof(buffer));
bytes_received = recv(client_socket, buffer, sizeof(buffer), 0); bytes_received = recv(client_socket, buffer, sizeof(buffer), 0);
if (bytes_received <= 0) { if (bytes_received <= 0 || bytes_received > BUFFER_SIZE) {
break; break;
} }
@@ -60,7 +65,7 @@ void Server::handle_client(int client_socket, bool mode, int steps) {
int direction = -1; int direction = -1;
int new_position = current_position; int new_position = current_position;
if (command == "вперёд") { if (command == "вперед") {
direction = 0; direction = 0;
new_position = current_position + 3; new_position = current_position + 3;
} else if (command == "направо") { } else if (command == "направо") {
@@ -89,14 +94,14 @@ void Server::handle_client(int client_socket, bool mode, int steps) {
maze.set_moves_left(moves_left - 1); maze.set_moves_left(moves_left - 1);
if (!check_status(maze)) if (!check_status(maze))
break; break;
response = "там стена, осталось " + std::to_string(maze.get_moves_left()) + " ходов\n"; response = "там стена, осталось " + std::to_string(maze.get_moves_left()) + " ходов";
send(client_socket, response.c_str(), response.size(), 0); send(client_socket, response.c_str(), response.size(), 0);
continue; continue;
} else if (maze.is_wall(current_position, direction)) { } else if (maze.is_wall(current_position, direction)) {
maze.set_moves_left(moves_left - 1); maze.set_moves_left(moves_left - 1);
if (!check_status(maze)) if (!check_status(maze))
break; break;
response = "там стена, осталось " + std::to_string(maze.get_moves_left()) + " ходов\n"; response = "там стена, осталось " + std::to_string(maze.get_moves_left()) + " ходов";
send(client_socket, response.c_str(), response.size(), 0); send(client_socket, response.c_str(), response.size(), 0);
continue; continue;
} else { } else {
@@ -114,7 +119,7 @@ void Server::handle_client(int client_socket, bool mode, int steps) {
int x = current_position % 3; int x = current_position % 3;
int y = current_position / 3; int y = current_position / 3;
std::string text("(" + std::to_string(x) + ", " + std::to_string(y) + ")\n"); std::string text("(" + std::to_string(x) + ", " + std::to_string(y) + ")");
response = "успешно, осталось " + std::to_string(maze.get_moves_left()) + " ходов. Вы находитесь в " + text; response = "успешно, осталось " + std::to_string(maze.get_moves_left()) + " ходов. Вы находитесь в " + text;
send(client_socket, response.c_str(), response.size(), 0); send(client_socket, response.c_str(), response.size(), 0);
} }
@@ -124,7 +129,7 @@ void Server::handle_client(int client_socket, bool mode, int steps) {
response = "вы проиграли\n"; response = "вы проиграли\n";
send(client_socket, response.c_str(), response.size(), 0); send(client_socket, response.c_str(), response.size(), 0);
} }
shutdown(client_socket, SHUT_RDWR);
close(client_socket); close(client_socket);
std::cout << "Игрок " << player_name << " отключился" << std::endl; std::cout << "Игрок " << player_name << " отключился" << std::endl;
} }
@@ -133,7 +138,7 @@ void Server::start(int steps, bool service_mode) {
if (service_mode) { if (service_mode) {
std::cout << "Service mode is ON" << std::endl; std::cout << "Service mode is ON" << std::endl;
} }
int new_socket; int new_socket;
if (listen(server_socket, MAX_CLIENTS) < 0) { if (listen(server_socket, MAX_CLIENTS) < 0) {
@@ -150,4 +155,5 @@ void Server::start(int steps, bool service_mode) {
std::cout << "Новое соединение установлено" << std::endl; std::cout << "Новое соединение установлено" << std::endl;
std::thread(&Server::handle_client, this, new_socket, service_mode, steps).detach(); std::thread(&Server::handle_client, this, new_socket, service_mode, steps).detach();
} }
shutdown(server_socket, SHUT_RDWR);
} }

View File

@@ -1,11 +1,36 @@
/*! @file server_main.cpp
Главный файл сервера для игры в лабиринт на базе сокетов.
@author ParkSuMin
@date 2025.04.30 */
#include "server.hpp" #include "server.hpp"
/*! Главная функция сервера.
@details Обрабатывает аргументы командной строки, создаёт экземпляр сервера и запускает его.
@throw std::runtime_error При ошибке работы сервера.
@throw std::exception При непредвиденной ошибке.
@dot
digraph main {
ranksep=0.25;
node [shape=box,fontsize="10",fixedsize=true,width=2,height=0.3]
edge [arrowsize=0.5]
Beg [label="Начало",shape=ellipse]
End [label="Конец",shape=ellipse]
A [label="Проверка аргументов"]
B [label="Создание сервера"]
C [label="Запуск сервера"]
D [label="Обработка исключений"]
Beg->A->B->C->End
B->D
C->D->End
}
@enddot */
int main(int argc, char **argv) { int main(int argc, char **argv) {
int opt; int opt;
std::string host = "localhost"; std::string host = "localhost";
int steps = 10; int steps = 10;
bool service_mode = false; bool service_mode = false;
short unsigned port = 1024u; short unsigned port = 2000u;
while ((opt = getopt(argc, argv, "h:p:sn:")) != -1) { while ((opt = getopt(argc, argv, "h:p:sn:")) != -1) {
switch (opt) { switch (opt) {