Подтверждение надежности с использованием UDP

16

У меня есть вопрос по поводу UDP. Для контекста, я работаю над игрой в реальном времени.

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

Что происходит, когда подтверждение сбрасывается?

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

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

Я чувствую, что моя основная логика здесь верна, что оставляет мне два варианта.

  1. Отправьте один пакет подтверждения и надейтесь на лучшее.
  2. Отправьте несколько пакетов подтверждения (возможно, 3-4) и надейтесь на лучшее, предполагая, что не все они будут отброшены.

Есть ли ответ на эту проблему? Я что-то принципиально неправильно понимаю? Есть ли какая-то гарантия использования UDP, о которой я не знаю? Я не решаюсь продвигаться вперед со слишком большим количеством сетевого кода, пока не почувствую себя комфортно, если моя логика будет здравой.

Grimelios
источник
11
Возможно, вам не хватает идей о «тайм-аутах» и «повторных попытках».
Кромстер говорит, что поддерживает Монику
Я мог бы быть, конечно. Вы предполагаете, что моя логика верна и, звучит не слишком негативно, но при сетевом программировании я не могу взять на себя никаких гарантий практически для любой части сетевой информации? Во время игры в реальном времени это тонна потенциально пропущенной информации, и это хорошо, но я просто хочу убедиться, что понимаю проблему.
Гримелиос
10
Никаких гарантий вообще. Правильно. Никогда не включайте «надежду» в свои алгоритмы. Они должны обрабатывать любые неудачные комбинации. PS Мы просто переключились на TCP в нашей RTS, где позаботились о eberything, так как нам нужна надежная связь (для симуляции слежения).
Кромстер говорит, что поддерживает Монику
5
Используйте TCP, когда нужна надежность, используйте UDP, когда это не имеет значения. Например, координаты игрока отправляются в моей игре через UDP. Я использую интерполяцию и сглаживание, чтобы сгладить любые пропущенные пакеты. работает как шарм. Вещи, которые действительно должны быть надежными, но могут быть немного медленнее, отправляются через TCP. Если у вас есть состояние, но более новое состояние лишает законной силы старое состояние, UDP является хорошим выбором, потому что не имеет значения, когда что-то промежуточное было отброшено 8e.g. Положение игрока).
Полигном
Это не прямой ответ на ваш вопрос, но я настоятельно рекомендую требовать подтверждения только в игре в реальном времени, когда они абсолютно необходимы (например, при первоначальном подключении). Намного проще (и надежнее) спроектировать и клиента, и сервер так, чтобы они «работали с тем, что у них было», пока они не получат новый пакет в системе без состояний, если вы можете. Quake 3 сделал это невероятно хорошо с системой на основе снимков . Также библиотеки ENet могут отправлять только определенные пакеты надежно, за исключением тех случаев , когда вам действительно нужно
JRH

Ответы:

32

Это форма проблемы двух генералов , и вы правы - никакого количества повторов недостаточно для полной гарантии получения.

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

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

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

Я внезапно хочу начать называть это «Реплика Симба» за то, что она игнорирует проблемы в прошлом и пытается жить в настоящем моменте. ;)

Рафики излагают некоторые доводы до абсурда об этой жизненной философии

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

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

ДМГригорий
источник
1
+1, хорошо написано. Я бы просто подчеркнул, что это больше относится к играм в жанре экшн / реального времени. В играх TBS и RTS (и некоторых событиях в жанре экшн) другой взгляд на «временной горизонт, за которым информация на самом деле не имеет значения».
Кромстер говорит, что поддерживает Монику
3
Да, для пошаговой игры я бы подумал, что можно будет использовать TCP, а не пытаться накатить собственный уровень надежности поверх UDP. ;) Я бы по-прежнему классифицировал микро в RTS как тип игрового процесса с жестким временным горизонтом, хотя - этот гибридный подход мог бы преуспеть там, где у вас есть как обновления с малой задержкой для высокой температуры, так и сеть безопасности для обратной обработки критически важных пропущенных событий, таких как расходование ресурсов.
DMGregory
Это очень полезно и отчасти подтверждает мою первоначальную озабоченность. Большое спасибо.
Гримелиос
2
Также может быть полезно упомянуть прямое исправление ошибок. Разработайте свой протокол таким образом, чтобы получатель мог самостоятельно определить, что пакет был отброшен при получении следующего пакета, добавив некоторые дополнительные данные, чтобы сгладить требуемую интерполяцию. Это может быть полезно, потому что часто пакеты UDP не заполнены, и вы просто отправляете меньшие пакеты чаще, чтобы уменьшить задержку. Добавление дополнительных байтов не повредит задержке, и пропускная способность в этих случаях не является проблемой.
MSalters
@MSalters Я бы сказал, что стоит остановиться на своем собственном ответе, если вы готовы. Я бы за это проголосовал. :)
DMGregory
9

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

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

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

user253751
источник
Важной особенностью TCP является то, что отправителю не нужно заботиться о том, был ли подтвержден какой-либо конкретный пакет, но в основном нужно заботиться о «высокой отметке» и о том, как долго пакеты находились в очереди без перемещения верхней отметки.
суперкат
1
@supercat Я бы не сказал, что это важно; больше похоже на оптимизацию.
user253751
Что касается вещи в скобках (отправка ACK для пакетов, которые вы уже получили), я думаю, вы должны подчеркнуть это вместо того, чтобы заключить в скобки. Кажется, он отсутствует в понимании ФП (или, по крайней мере, в его описании).
Angew больше не гордится SO
@ Ангел готов.
user253751
6

Если вы хотите заново изобрести TCP, имеет смысл сначала взглянуть на TCP , который касается именно той проблемы, которую вы описываете (часть решения заключается в использовании пользовательских значений для попыток повторных попыток и тайм-аутов).

Решения, которые используют 2 канала, канал TCP (для надежной связи), а также канал UDP (для связи с низкой задержкой), не являются редкостью.

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

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

Питер
источник
3

В RTS вы действительно не можете использовать протокол, такой как TCP, и не можете сделать UDP надежным. Если вы попытаетесь это сделать, игра будет зависать при каждом взлете сети.

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

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

Тогда возникает вопрос: что вы делаете, когда пакет пропадает? И ответ ... вы думаете. Игрок, вероятно, движется по прямой линии, верно? Просто переместите их на один шаг дальше по этой линии. ... За исключением того, что ни один игрок RTS никогда не движется по прямой линии. И тогда есть обнаружение столкновения.

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

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

Стиг Хеммер
источник
Warcraft 3 использует TCP.
fsp