Nagle-подобная проблема - PullRequest
       36

Nagle-подобная проблема

0 голосов
/ 07 июля 2011

так что у меня есть игра в реальном времени, с сервером C ++ с отключенным nagle, использующим SFML-библиотеку , и клиент, использующий asyncsocket , также отключает nagle.Я отправляю 30 пакетов каждую 1 секунду.Нет проблем при отправке с клиента на сервер, но при отправке с сервера на клиенты некоторые пакеты переносятся.Например, если я посылаю «a» и «b» в совершенно разных пакетах, клиент читает их как «ab».Это происходит только один раз, но это создает реальную проблему в игре.

Так что мне делать?Как я могу решить это?Может быть, это что-то на сервере?Может быть, настройки ОС?

Для ясности: я НЕ пользуюсь nagle, но у меня все еще есть эта проблема.Я отключил как на клиенте, так и на сервере.

Ответы [ 2 ]

3 голосов
/ 07 июля 2011

Например, если я посылаю «a» и «b» в совершенно разных пакетах, клиент читает их как «ab».Это происходит только один раз, но это создает реальную проблему в игре.

Я думаю, вы упустили из виду фундаментальную природу TCP: это потоковый протокол, а не пакетный протокол.TCP не уважает и не сохраняет границы данных отправителя.Другими словами, TCP может свободно объединять (или разбивать!) Отправляемые вами «пакеты» и представлять их на приемнике так, как он хочет.Единственное ограничение, которое соблюдает TCP, заключается в следующем: если байт доставлен, он будет доставлен в том же порядке, в котором он был отправлен.(И ничто в Nagle не меняет этого.)

Итак, если вы дважды вызываете send (или write) на сервере, отправляя эти шесть байтов:

"packet" 1: A B C
"packet" 2: D E F

Ваш клиентсторона может recv (или read) любую из следующих последовательностей байтов:

ABC / DEF
ABCDEF
AB / CD / EF

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

Как уже говорили другие, есть много способов сделать это.Вы можете, например, отправить новую строку после каждого кванта информации.Так (частично) работают HTTP, FTP и SMTP.

Вы можете отправить длину пакета вместе с данными.Обобщенная форма для этого называется TLV, для «Тип, Длина, Значение».Отправьте поле типа фиксированной длины, поле фиксированной длины, а затем значение произвольной длины.Таким образом, вы узнаете, когда вы прочитали все значение и готовы к следующему TLV.

Вы могли бы договориться, что каждый отправленный вами пакет идентичен по длине.

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

1 голос
/ 07 июля 2011

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

EDIT2

Поскольку вы запрашиваете протокол, вот как я это сделаю:

  • Определить заголовок для сообщения. Допустим, я бы выбрал 32-битный заголовок.

    Header:
        MSG Length: 16b
        Version: 8b
        Type: 8b
    
  • Затем приходит настоящее сообщение с MSG Length байтами.

Итак, теперь, когда у меня есть формат, как бы я справился?

Сервер

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

Клиент

Я постоянно получаю информацию с сервера, верно? Поэтому я должен сделать что-то вроде read.

  • Чтение байтов с сервера. Любая сумма может прибыть. Продолжайте читать, пока не получите хотя бы 4 байта.
  • Получив эти 4 байта, интерпретируйте их как заголовок и извлеките MSG Length
  • Продолжайте читать, пока не получите хотя бы MSG Length байтов. Теперь вы получили ваше сообщение и можете его обработать

Это работает независимо от параметров TCP (таких как NODELAY), ограничений MTU и т. Д.

...