UDP неблокирует или отдельный поток для получения?

10

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

Или лучше использовать другой метод, например, асинхронные сокеты? Лучшие методы всегда приветствуются!

Янник Ланге
источник

Ответы:

6

Хорошо, во-первых, если у вас есть что-то, и оно работает, обычно хорошая идея оставить это так. Зачем исправлять то, что не сломано?

Но если у вас возникли проблемы, и вы действительно хотите переписать свой сетевой код, я думаю, у вас есть четыре основных варианта:

  1. Многопоточный код блокировки (что вы сейчас делаете)
  2. Неблокирующие розетки с уведомлением о срабатывании уровня
  3. Неблокирующие розетки с уведомлением об изменении готовности
  4. Асинхронные розетки

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

В частности, я бы посоветовал не блокировать сокеты в многопоточной среде, поскольку это обычно приводит к блокировке и другим функциям синхронизации, которые не только значительно увеличивают сложность кода, но также могут ухудшать его производительность, поскольку некоторые потоки ожидают другие.

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

Неблокирующее программирование сокетов немного утомительно, если вы еще не пробовали его, но оказывается, что это довольно просто, и если вы уже проводите опрос ввода, у вас уже есть способ делать неблокирующие сокеты.

Первое, что вы хотите сделать, это установить сокет в неблокирующее состояние. Вы делаете это с fcntl().

После этого, прежде чем делать send(), recv(), sendto(), recvfrom(), accept()( connect()немного отличается) или другие вызовы , которые могут блокировать поток, вы звоните select()на сокете. select()сообщает вам, может ли последующая операция чтения или записи быть выполнена на сокете без его блокировки. Если это так, вы можете безопасно выполнить нужную операцию, и сокет не заблокируется.

Включить это в игру довольно просто. Если у вас уже есть игровой цикл, например, вот так:

while game_is_running do
    poll_input()
    update_world()
    do_sounds()
    draw_world()
end

Вы можете изменить это, чтобы выглядеть так:

while game_is_running do
    poll_input()
    read_network()
    update_world()
    do_sounds()
    write_network()
    draw_world()
end

где

function read_network()
    while select(socket, READ) do
        game.net_input.enqueue(recv(socket))
    end
end

а также

function write_network()
    while not game.net_output.empty and select(socket, WRITE) do
        send(socket, game.net_output.dequeue())
    end
end

С точки зрения ресурсов, единственная книга, которую я думаю, что каждый должен иметь на своих книжных полках, даже если это единственная книга, которую они имеют, это " Сетевое программирование Unix, Том 1 " покойного Ричарда Стивенса. Неважно, занимаетесь ли вы Windows или другой ОС или программированием языковых сокетов. Не думайте, что понимаете сокеты, пока не прочитаете эту книгу.

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

Панда Пижама
источник
Я только начал свой игровой движок, поэтому переписывание не проблема. Этот ответ был действительно полезен, и спасибо за рекомендации книги. Вы также знаете книгу, которая более конкретно описывает сетевые возможности в играх?
Янник Ланге
3
@YannickLange: Нет, но я бы не советовал вам искать книги, относящиеся к игре, поскольку сетевое взаимодействие в значительной степени не зависит от жанра, а также ни одна другая книга не соответствует уровню книги Стивенса. Слава за использование UDP, хотя использование протокола, ориентированного на сообщения, является хорошей идеей при написании программ, ориентированных на сообщения (как и большинство игр). Многие люди обычно заканчивают тем, что пишут абстракцию сообщений через TCP, которая сама по себе является потоково-ориентированной абстракцией, построенной на основе протокола, ориентированного на сообщения (IP).
Панда Пижама
-1

Вы не писали, какой язык программирования вы используете, но большинство языков предоставляют хорошие платформы асинхронных сокетов, которые часто используют функции базовой операционной системы.

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

Поэтому я бы порекомендовал вам использовать асинхронные сокеты.

Philipp
источник
4
зачем ему нужна нить для каждого сокета? Особенно с асинхронными сокетами? Многие тяжеловесные серверы используют модель «пула потоков» для обработки данных сокетов, где каждый поток обслуживает N клиентов, и они создаются или уничтожаются по мере необходимости. Просто подумайте, что этот ответ нуждается в улучшении: D
Гримшоу
@ Гримшоу, я думаю, ты неправильно понял мой ответ. Я думаю, что теперь пояснил, что многопоточная модель потребуется при использовании блокирующих сокетов.
Филипп
@ Филипп Я не сказал, какой язык программирования я использую, потому что я думал, что это не очень важно для вопроса (кстати, я использую c ++). Я благодарю вас за вашу рекомендацию использовать асинхронный сокет. Знаете ли вы рекомендуемую книгу / веб-страницу для изучения этих методов в игре (движках)?
Янник Ланге
@Grimshaw: А как каждый поток обслуживает Nклиентов? Если вы блокируете и хотите убедиться, что каждый сокет обрабатывает свои данные независимо от состояния остальных сокетов, у вас должен быть один поток на сокет. Это, или не используйте блокирующие сокеты.
Панда Пижама
Мой комментарий был к первой версии вашего ответа, между тем вы редактировали и исправляли его, забудьте про то, что я сказал: D
Grimshaw