Использование TCP для команд в реальном времени:Алгоритм Нейгла вызывает огромные задержки, что мне делать?

StackOverflow https://stackoverflow.com/questions/1609880

Вопрос

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

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

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

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

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

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. двигаться вперед отправьте клиентом (момент:0), полученный клиентом B (момент:0)

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

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

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

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

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

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

  8. прекратить поворот отправить клиентом (момент: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.Тем не менее, у меня есть погуглил (обратите внимание, что его предложение о частичном сообщении tcp реализовано в моей системе, но не имеет ничего общего с синхронизацией) немного и заметил, что люди категорически не рекомендуют отключать Nagle.

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

Заранее благодарю.- Том

Это было полезно?

Решение

Вы хотите использовать TCP_NODELAY, чтобы отключить алгоритм nagle для этого сокета.В современных операционных системах существуют настройки для отключения его на общесистемной основе, и это ЯВЛЯЕТСЯ неодобрительно, но нет причин, по которым вы не должны делать это с сокетом, для которого, как вы знаете, вам нужна низкая задержка.

setsockopt(TCP_NODELAY) setsockopt(TCP_NODELAY)

Другие советы

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

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

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

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

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

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

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

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

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

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

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

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

Таким образом, клиенты постоянно повторно синхронизируются.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top