Я отправил онлайн-видеограмму (grid-based), которая использует протокол TCP для обеспечения надежной связи в топологии сети сервера и клиента. Моя игра работает достаточно хорошо, но страдает более высокой, чем ожидалось, латентностью (аналогичные игры в жанре TCP, похоже, лучше справляются с минимальной задержкой). Р>
Во время расследования я обнаружил, что время ожидания только неожиданно велико для клиентов, работающих под управлением Microsoft Windows (в отличие от клиентов Mac OS X ). Кроме того, я обнаружил, что если клиент Windows устанавливает TcpAckFrequency=1
в реестре и перезагружает их машину, их латентность становится нормальной.
Похоже, что мой дизайн сети не учитывал задержанное подтверждение:
Конструкция, которая не учитывает взаимодействие с задержкой подтверждения , алгоритм Нэгла и буферизация Winsock, может резко повлиять на производительность. ( Ссылка )
Однако мне кажется, что почти невозможно принять во внимание задержку подтверждения в моей игре (или любой игре). Согласно MSDN, стек Microsoft TCP использует следующие критерии, чтобы решить, когда отправлять один ACK на принятые пакеты данных:
- Если второй пакет данных принят до истечения таймера задержки (200 мс), отправляется ACK.
- Если есть данные, которые должны быть отправлены в том же направлении, что и ACK до того, как будет принят второй пакет данных и истечет таймер задержки, ACK связывается с сегментом данных и отправляется немедленно.
- Когда таймер задержки истекает (200 мс), отправляется ACK.
( Ссылка )
Считая это, можно предположить, что обходной путь для отложенного подтверждения в стеке TCP от Microsoft выглядит следующим образом:
- Отключите алгоритм Nagle (TCP_NODELAY).
- Отключите буфер отправки сокета (
SO_SNDBUF
= 0), чтобы можно было ожидать, что вызовsend
отправит пакет. - При вызове
send
, если дальнейшие данные не будут отправлены немедленно, вызовитеsend
снова с однобайтовым количеством данных, которое будет отброшено получателем.
При таком подходе второй пакет данных будет приниматься приемником примерно в то же время, что и предыдущий пакет данных. В результате ACK
должно немедленно отправляться от получателя к отправителю (эмулируя, что TcpAckFrequency=1
делает в реестре).
Тем не менее, из моего тестирования это улучшенная задержка составляет лишь примерно половину того, что делает редактирование реестра. Что мне не хватает?
р> В: Почему бы не использовать UDP?
A: Я выбрал TCP, потому что каждый отправляемый пакет должен прибыть (и быть в порядке); нет пакетов, которые не требуют повторной передачи, если они теряются (или становятся неупорядоченными). Только когда пакеты могут быть отброшены / неупорядочены, UDP может быть быстрее TCP!