Использование TCP для команд в реальном времени: алгоритм Nagle вызывает огромные задержки, что мне делать? - PullRequest
3 голосов
/ 23 октября 2009

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

Это пример проблемы, вызванной арифметикой Нейгла:

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

Первый - это корабль, который я передвинул (двинул вперёд + вправо, вперёд был получен, но не прав)

Клиент, отправляющий команды:

84796: Sending data: 2#4
84796: Sending data: 2#2
84904: Sending data: 2#3
84904: Sending data: 2#0
86187: Sending data: 2#4
86188: Sending data: 2#2
86374: Sending data: 2#3
86404: Sending data: 2#0

Клиент, получающий команды:

79244: Raw receive: 3#3#4$
79244: New command: 3#3#4
79398: Raw receive: 3#3#2$3#3#3$3#3#0$
79399: New command: 3#3#2
79399: New command: 3#3#3
79399: New command: 3#3#0
80635: Raw receive: 3#3#4$
80635: New command: 3#3#4
80908: Raw receive: 3#3#2$3#3#3$3#3#0$
80908: New command: 3#3#2
80908: New command: 3#3#3
80908: New command: 3#3#0

«момент» - это странный термин, который не означает, что я пытаюсь сказать, но здесь, кажется, количество времени в миллисекундах после предыдущей команды

  1. двигаться вперед отправлено клиентом A (момент: 0), получено клиентом B (момент: 0)

  2. повернуть направо отправлено клиентом A (момент: 0), получено клиентом B (момент: 155)

  3. прекратить движение отправлено клиентом A (момент: 108), получено клиентом B (момент: 0)

  4. прекратить поворот отправлено клиентом A (момент: 0), получено клиентом B (момент: 0)

  5. двигаться вперед отправлено клиентом A (момент: 1283), получено клиентом B (момент: 1236)

  6. повернуть направо отправлено клиентом A (момент: 1), получено клиентом B (момент: 273)

  7. остановка движения отправлено клиентом A (момент: 186), получено клиентом B (момент: 0)

  8. остановить поворот отправлено клиентом A (момент: 30), получено клиентом B (момент: 0)

Это таблица команд, соответствующая командам:

Клиент-> Сервер

2# (movement info)
0) now not turning
1) now turning left
2) now turning right
3) now not moving
4) now moving forward

Сервер-> Клиент

3# (movement info)
[shipId]#
0) now not turning
1) now turning left
2) now turning right
3) now not moving
4) now moving forward

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

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

Правда ли, что я не должен отключать алгоритм Nagle по этой причине и должен вместо этого искать другое решение? Почему (нет)?

Заранее спасибо. - Том

Ответы [ 5 ]

5 голосов
/ 23 октября 2009

Вы хотите использовать TCP_NODELAY, чтобы отключить алгоритм Nagle для этого сокета. В современных ОС существуют настройки для его отключения в масштабе всей системы, и IS не одобряется, но нет причины, по которой вы не должны делать это с сокетом, который ЗНАЕТЕ, что вам нужна низкая задержка.

setsockopt (TCP_NODELAY)

2 голосов
/ 23 октября 2009

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

Однако обратите внимание, что TCP обеспечивает гарантию доставки и заказа , тогда как UDP не предоставляет ни одного.

Эта точка, по-видимому, является критически важной точкой в ​​этом приложении, поэтому вам придется наложить свой собственный механизм упорядочивания команд подтверждения / повтора поверх.

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

RFC1006 формализует механизм отправки пакетов по потоку TCP.

1 голос
/ 01 ноября 2009

Обратите внимание, что Windows реализует ACK с задержкой , а также Nagle. Это может дать вам дополнительные 200 мс задержки на отдельные сообщения TCP. Насколько я знаю, вы не можете изменить это в Windows, кроме как с помощью ключа реестра (и в некоторых случаях «хотпатч» для включения ключа реестра).

1 голос
/ 23 октября 2009

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

  • Поверните налево
  • Подождите 100 мс
  • Хватит поворачиваться

что клиент B увидит ту же самую задержку в 100 мс. Буферизация Nagle только делает это более экстремальным - но всегда будет дрожание и различные задержки.

Вам действительно нужно отправить что-то вроде:

  • (Я нахожусь в положении X1, Y1 с заголовком Z1) Поверните налево
  • (Я нахожусь в положении X2, Y2 с заголовком Z2) Остановить поворот

Так что клиенты постоянно ресинхронизируются.

1 голос
/ 23 октября 2009

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

Взгляните на RTP (который работает по UDP) для более выживаемого в сети подхода.

...