Танчики в консоли, статья третья: «Сервер и клиент»

Танчики в консоли, статья третья: «Сервер и клиент»

Начну пожалуй с того, что снова всё переписала, но это не коснулось структур. И за прошедшее время сделала много изменений (как и в сервере, так и в клиенте), но пока остаётся ещё пару аспектов (я не сделала программу которая их строит и расставляет начальные позиции игроков (пока что всё вручную)), которые будут устранены в ближайшее время.

Я слышу критику и стараюсь написать интересную статью с разбором этого проекта.

Глава 1: «Рабочий клиент»

Хорошее название, да? Но за ним скрыт смысл клиента. Я хотела сделать небольшой костяк, но со временем пришли экзамены и рефакторинг стал немного труден.

Перед тем как мы начнём разбирать клиент (по КОДсточкам) я должна рассказать как взаимодействует наш клиент-сервер:

1. Клиент говорит: «Хей, сервер, я пришёл к тебе!». 2. Сервер ему отвечает: «Хорошо, клиент, вот тебе координаты стен и игроков». 3. Клиент снова говорит: «Теперь то мы пообщаемся».

И так между ними возникла… связь на TCP сокетах.

Главные методы были изменены и сюрприз -> мы стартуем из другого пространства имён и класса. В дальнейшем я и это переделаю (я помню что обещала разбить всё на разные файлы, но в связи с праздниками и экзаменами это сделать оказалось трудным, поэтому и прибегнула к другому пространству имён).

Основные переменные, которые собственно и работают в схеме выше — это порт и адрес сервера.

Клиент условно можно разделить на две группы: 1-я, это обслуживающая группа, т.е. функции выполняющие расчеты и печатающие нам в консольку сообщения с сервера. 2-я, это группа из нашего алгоритма взаимодействия (что я указала выше).

Обслуживающая группа и всё-всё-всё

Эта группа, которая в основном в первом пространстве имён, в неё входят такие классы/структуры, как:

Я их уже рассматривала постами ранее, я ничего не изменила в них (кроме пару полей, сделала их публичными).

Изменив название, не изменился смысл метода — мы печатаем танк в зависимости от его координат:

И наш основной метод:

Этот метод делает огромную работу — он обрабатывает нажатые клавиши, собирает данные через другие методы (что находятся в структурах) и посылает их в метод отправки на сервер.

Сетевая группа

Группа методов, которая общается с сервером.

Первый метод (Eventlistener()) запускается во втором потоке и слушает сервер, в то время как основной поток обрабатывает нажатые клавиши и отправляет изменённые данные на сервер (с помощью метода MessageToServer()). Остальные же методы используются только при запуске/завершение работы клиента.

Глава 2: «Сервер-велосипед»

Наш сервер (основная его часть) работает в многопоточном режиме, т.е. многопоточное считывание и отправка в несколько потоков.

Интересный факт, при максимальной загруженности (будем считать что это 6 человек) количество одновременно запущенных потоков (и на чтение и на отправку) равно 6 на чтение, и 6*6 = 36 — на одновременную передачу всем (сумма — 42), что вроде бы логично, но в реальности клиент может делать по 2-4 действия в секунду (учитывая пинг), что умножает количество потоков (на передачу) соответственно на 2-4.

То есть мы получаем формулу: Count+Count*i+1, где Count — кол-во пользователей, i — кол-во одновременно совершаемых действий и +1, потому что мы учитываем основной поток.

Почему многопоточное считывание? мне так удобно, для меня гораздо легче разбить всё на разные потоки и обрабатывая с них информацию отправлять клиентам и ещё один бонус — мы не рвём коннект с клиентом, что очень важно (ибо если мы не поступаем так, то на стороне клиента порт перестаёт передавать данные и отключается (для каждой следующей отправки используется следующий порт)).

Связь между потоками реализована кортежем Передатчик-Приёмник, что создаётся путём вызова функции std::sync::mpsc::channel() из стандартной библиотеки.

Но у этого метода есть ограничения, ибо нельзя Передатчику не передавать (говорить) сообщения на приёмник. Т.к. компилятор не знает какой тип используется в передаче сообщений.

Для чего нам нужен первый поток и зачем Передатчик-Приёмник? Это распараллеливание потоков, чтобы основной поток создавал потоки для отправки данных по всем адресатам.

То есть мы получаем схему:

Где квадратики — это отдельный потоки, а стрелка от одного к другому — это метод .send() в Передатчике (то есть отправляем данные на приёмник).

Но в потоке, что принимает данные есть много потоков (как мы видели из формулы выше), полная схема будет выглядеть так:

Начнём разбор этого всего. Нам необходимо считывать данные с файлов перед запуском сетевого взаимодействия, надо же что-то отправлять и карты не должны быть вшиты в сервер (ведь мы будем писать программу для создания этих самых карт).

Я использую функции из своего lib.rs (mod Text) для считывания и обработки файлов.

Небольшая схема работы нашего сервера:

И мы её из main вызываем:

Вот такой получился велосипед, в дальнейшем я добавлю в эту статью (и в свой репозиторий) сервер на c#.

Заключение!

Что мне не удалось:

1. Версия на WinForm. 2. Программа для визуального создания уровней. 3. Начало матча через n-секунд при достижение минимально возможного количества игроков.

Жду ваших пожеланий и исправлений, огромное спасибо за критику и всего вам наилучшего!

📎📎📎📎📎📎📎📎📎📎