Нэглеподобная проблема

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

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

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


person Eli    schedule 07.07.2011    source источник


Ответы (2)


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

РЕДАКТИРОВАТЬ2

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

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

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

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

Сервер

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

Клиент

Я постоянно получаю данные с сервера, верно? Так что я должен сделать что-то вроде read.

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

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

person cnicutar    schedule 07.07.2011
comment
Я отключил оба. Я проверил SCTP, и TCP кажется лучше для игр MMO в реальном времени. - person Eli; 07.07.2011
comment
@Eli как лучше? - person cnicutar; 07.07.2011
comment
Здесь вопрос не в этом, сейчас это моя проблема, может быть, если я решу ее изменить, мне потребуется много времени, чтобы изменить весь мой код, поэтому сейчас я хочу решить эту проблему, потому что это не должно быть как это. - person Eli; 07.07.2011
comment
@Eli Так что не кажется лучше. Я знал, что это неудобно, но я пытался спровоцировать тебя на то, чтобы ты признал, что у тебя на самом деле нет аргументов :-) - person cnicutar; 07.07.2011
comment
Я не понимаю. Большинство MMO-игр в реальном времени используют TCP без этой проблемы, так что мне нужно использовать SCTP? Конечно, я могу отредактировать свой код, чтобы эта проблема с TCP работала лучше, но так и должно быть на самом деле? - person Eli; 07.07.2011
comment
@Eli Я на твоей стороне, но почему ты не можешь просто получить «ab» и относиться к нему так, как будто есть 2 сообщения ?! - person cnicutar; 07.07.2011
comment
Что ты имеешь в виду? Откуда мне знать, что аб это 2 сообщения а не одно? - person Eli; 07.07.2011
comment
@Eli Использовать разделитель? |a||b| Использовать какой-то протокол с длиной заголовка? Варианты бесконечны. - person cnicutar; 07.07.2011
comment
ОК, я рассматриваю возможность изменения своего протокола. Я изменил свой код, но теперь у меня ОГРОМНЫЕ промежутки между любыми пакетами. Я не знаю почему. Так что было бы лучше для позиций? UDP? Не могли бы вы немного рассказать о SCTP? - person Eli; 07.07.2011
comment
Nagle не задерживает первый пакет (из ряда пакетов). Он также не задерживает пакеты, если получатель быстро подтверждает предыдущие пакеты. Так что отключение Nagle здесь мало помогает. Это просто может немного увеличить шансы получить данные в тех же пакетах, в которых вы их отправили. - person hagello; 20.08.2015
comment
@hagello Да. Упоминание Нэгла в этом было чушью, беру свои слова обратно. Если подумать, я бы удалил весь этот ответ, если бы мог. - person cnicutar; 20.08.2015

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

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

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

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

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

ABC / DEF
ABCDEF
AB / CD / EF

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

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

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

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

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

person Robᵩ    schedule 07.07.2011