Избранное Все заметки Главная
1 заметка с тегом

games

Архитектура сессионных(комнатных) сетевых игр

Сессионные игры — игры,в которых каждый отдельно взятый игрок может создать своё лобби,пригласить или подождать подключения других игроков и начать играть с подключенными к лобби игроками.

Вопрос:
Пишу небольшую мультиплеерную настольную игру. Каждый «стол» игры планируется в отдельном потоке. Возник вопрос по организации приема данных от клиентов (соединение UDP): Все потоки слушают 1 порт и каждый ловит из общей массы нужную для стола инфу или же назначать порт для каждого отдельного стола избавляя от функции сортировки данных?

Ответ:
Порты и потоки — это очень ценный и ограниченный ресурс. Портов всего 65536 (и не все из них можно юзать), а потоков вообще нежелательно иметь больше чем процессорных ядер (упрощенно говоря). Что если к серверу подключится 100 клиентов? А если 1000? Дальше такой вопрос: если для каждой игровой сессии выделять отдельный порт, то откуда клиент будет знать на какой порт ему стучаться? Значит первоначальное соединение всё равно будет происходить через один фиксированный порт? Но тогда получается, что всё равно нужен механизм сортировки пакетов от разных клиентов. То есть его не избежать. Но и бояться этого не надо. Клиент просто должен каждый раз передавать ID сессии, а сервер легко найдет по нему нужную (это одна строчка кода).

Правильный подход будет приблизительно таким. Состояние каждой игровой сессии (стола) должно храниться в некоем объекте. При этом, никакого потока у сессии нет, просто хранится её состояние (ну типа чья очередь ходить, у кого сколько очков, кто где стоит) и всё. И таких вот объектов целый массив, по количеству столов. Один единственный поток слушает один единственный порт. Когда клиент обращается к серверу, он передает ID своей сессии. Сервер принимает пакет, и по ID находит в массиве нужную сессию. Вот тут, он запускает новый поток, и в этом потоке обрабатывает полученные данные. В результате обработки состояние сессии изменяется, клиенту при необходимости отправляется какой-то ответ, а поток обработки сразу после этого завершается. То есть суть в том, что новый поток запускается лишь тогда, когда нужно отреагировать на действия клиента, и не живет дольше чем надо.

Что это дает? Если у нас будет 1000 клиентов, то маловероятно, что все они одновременно сделают ход. Ну максимум 100 из них могут походить. Таким образом, в каждый момент времени у нас не 1000 запущенных потоков, а в 10 раз меньше (на практике будет еще меньше). В случае чего, можно легко добавить механизм, который будет ограничивать максимально допустимое количество потоков (скажем, не более 16), а «лишние» запросы выстраивать в очередь.
Возможно вы делаете любительский проект и там не будет ста клиентов одновременно. Тогда, если вы не хотите усложнять, то можете вообще не создавать новые потоки на каждый клиентский запрос, а обрабатывать всё в одном. Конечно клиентам придется ждать пока сервер ответит им по очереди. Но ИМХО это решение всё равно будет более верным с архитектурной точки зрения, чем держать долгоживущий поток на каждую сессию. Навряд ли время ожидания будет заметно человеку при количестве клиентов 10-20.

7 мая   games   room   udp