Какой протокол сокет-сервера эффективен?

Когда я писал простой сервер для простой многопользовательской игры клиент ‹> сервер, я подумал о следующем текстовом протоколе с использованием библиотеки перевода. По сути, каждая команда имела определенное значение, например:

1 = character starts turning right
2 = character starts turning left
3 = character stops turning
4 = character starts moving forward
5 = character stops moving
6 = character teleports to x, y

Таким образом, клиент будет просто транслировать следующее, чтобы сообщить, что игрок теперь движется вперед и поворачивает направо:

4
1

Или, чтобы телепортироваться на 100x200:

6#100#200

Где # — разделитель параметров.

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

Конечно, все данные будут проверены на стороне сервера, но это другой вопрос.

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

Однако большинство «профессиональных» фрагментов кода, которые я видел, по-видимому, отправляли объекты или команды xml. Мне кажется, что для этого требуется гораздо больше ресурсов сервера, не так ли?

Является ли моя неопытная логика того, почему мой текстовый протокол будет эффективным, ошибочна? Или какой протокол рекомендуется для многопользовательских игр в реальном времени?

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


person Tom    schedule 04.05.2010    source источник


Ответы (4)


Однако большинство «профессиональных» фрагментов кода, которые я видел, по-видимому, отправляли объекты или команды xml. Мне кажется, что для этого требуется гораздо больше ресурсов сервера, не так ли?

Является ли моя неопытная логика того, почему мой текстовый протокол будет эффективным, ошибочна? Или какой протокол рекомендуется для многопользовательских игр в реальном времени?

Обычный текст обходится дороже, чем двоичный формат, содержащий ту же информацию. Например, если вы отправляете только 1 байт, вы можете отправить только 10 различных команд, цифры от 0 до 9. Двоичный формат может отправить столько разных команд, сколько различных значений вы можете поместить в байт, т.е. 256.

Таким образом, хотя вы думаете об объектах как о больших, на самом деле они почти всегда меньше, чем простое текстовое представление того же объекта. Часто они настолько малы, насколько это возможно без сжатия (и вы всегда можете добавить сжатие).

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

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

person Kylotan    schedule 05.05.2010
comment
Мне часто предлагают начать с того, что у меня есть на данный момент, и изменить это позже. На самом деле я полностью перезапустил свой проект, и прежде чем я начну писать код, на этот раз я собираюсь задокументировать каждую тему, о которой мне нужно знать, в данном случае это то, какой протокол использовать для моей сети. Кажется, что двоичный формат является наиболее эффективным, так что это действительно то, что я хочу. У меня нет опыта отправки необработанных двоичных данных, не могли бы вы привести пример в java, actionscript или даже псевдокоде? Это завершит эту главу в моих поисках и позволит мне принять ваш ответ. - person Tom; 06.05.2010
comment
Боюсь, я недостаточно хорошо разбираюсь в Java или ActionScript, чтобы создавать псевдокод. Однако интерфейс java.io.DataOutput близок к тому, о чем мы говорим. Я считаю, что в Javascript и Actionscript вы можете преобразовать значение байта в односимвольную строку с помощью fromCharCode, я полагаю. - person Kylotan; 06.05.2010
comment
Не могли бы вы привести пример двоичной команды размером 1 байт? Я понятия не имею, как это сделать. - person Tom; 06.05.2010
comment
Вам нужно найти кого-то с большим опытом работы с этими языками, чтобы дать вам специфику. - person Kylotan; 07.05.2010

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

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

Кроме того, многие игры используют UDP для такого рода передачи данных вместо TCP. UDP ненадежен, поэтому вы можете не получить все свои пакеты. Это означает, что «остановить движение сейчас» или «начать движение сейчас» нельзя принимать парами. При кодировании поверх UDP еще более важно время от времени отправлять «это состояние прямо сейчас», чтобы у клиентов было достаточно возможностей для синхронизации.

person dash-tom-bang    schedule 04.05.2010
comment
Я только что привел пример, состояние позиции можно было бы указать и в моей системе. Например, 1#x#y будет информировать клиента о реальной позиции игрока, чтобы он мог корректировать отображение (плавно). Мой вопрос заключается в том, является ли текстовый протокол с библиотекой перевода более эффективным, чем другие методы. - person Tom; 04.05.2010
comment
Я хотел сказать, что обычно вы не хотите иметь дело с переходными состояниями. Текст или нет - на самом деле не имеет значения, если вы не отправляете много данных, но каждый символ занимает целый байт, независимо от того факта, что вы, вероятно, не используете весь диапазон значений. Так что, честно говоря, это довольно расточительно для памяти, но если это работает для того, что вы делаете, я бы не стал об этом беспокоиться. Синтаксический анализ текстовых данных также довольно медленный по сравнению с простым чтением байтов из сети и их использованием без каких-либо действий, кроме преобразования порядка следования байтов. Тем не менее, текст, безусловно, будет легче отлаживать! - person dash-tom-bang; 04.05.2010
comment
Я собираюсь полностью перезапустить свой проект, с нуля. Мне нужен более точный ответ, например, почему люди отправляют сериализованные объекты и как, в чем преимущество по сравнению с текстом. Или, может быть, мне следует отправлять команды в двоичном формате, как указал битк? - person Tom; 04.05.2010
comment
Что ж, если у вас есть что-то, что работает, нет необходимости все это выбрасывать! - person dash-tom-bang; 05.05.2010
comment
Я не хочу, не для того количества людей, которых я хочу иметь онлайн одновременно ;) - person Tom; 05.05.2010
comment
хорошо, тогда вы захотите отправить свои данные в виде необработанных данных. т.е. вместо отправки текста для 100 отправьте биты, которые представляют это. Помните также о сетевом порядке байтов, особенно если вы хотите поддерживать разнородные платформы. - person dash-tom-bang; 05.05.2010
comment
+1, читая этот пост, я осветил несколько связанную проблему. - person o0'.; 09.05.2010
comment
Какой протокол вы используете Lo'oris? - person Tom; 10.05.2010

Распространенным способом является использование двоичного формата, а не текстового или xml. Таким образом, всего одним байтом вы можете представить одну из 256 различных команд.

Также используйте UDP, а не TCP. Игра будет намного быстрее реагировать с UDP в случае потери пакетов. В случае потери пакетов вы все равно можете экстраполировать движения. С каждым пакетом отправьте номер пакета, чтобы сервер знал, когда была отправлена ​​команда.

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

редактировать:

Чуть не забыл... Google буферы протоколов могут очень помочь, когда отправка сложных структур данных.

person bitc    schedule 04.05.2010
comment
Как бы вы отправили 256 команд в 1 байте? Некоторый псевдокод был бы высоко оценен. Если я использую UDP, могу ли я воспроизвести функцию TCP для повторной отправки ошибочных пакетов? С вашим двоичным форматом вы по-прежнему используете библиотеку перевода, как я со своим текстовым форматом, верно? - person Tom; 04.05.2010
comment
@Tom: я был ужасно неясен. Один байт может представлять 256 различных команд. Таким образом, вы можете отправить один из 256 на байт. И да, с UDP вы можете, включив номер пакета, воспроизвести желаемую функцию TCP (получение данных в правильном порядке). Потеря пакетов может произойти в любое время. С TCP вы получаете очень высокую задержку. С UDP вы иногда можете получить меньшую задержку, но вместо этого потерять информацию, что обычно является хорошим компромиссом в онлайн-играх в реальном времени. - person bitc; 05.05.2010

Я подумал, что отдам свои пять копеек и предоставлю практическое применение тому, что называется двоичной сериализацией. Концепция на самом деле невероятно проста, но только снаружи кажется сложной.

На самом деле вы можете отправлять XML-файлы и иметь сервер, который обрабатывает данные в XML для различных функций внутри самого сервера. Вы также можете просто отправить серверу один номер, который хранится на сервере в виде переменной. После этого он может обработать остальные данные и выбрать правильный курс действий.

В качестве примера, немного грубого кода:

private const MOVE_RIGHT:int = 0; 
private const MOVE_LEFT:int = 1;
private const MOVE_UP:int = 2;
private const MOVE_DOWN:int = 3;

function processData(e:event.data)
{
    switch (e)
    {
       case MOVE_RIGHT:
       //move the clients player to the right

       case MOVE_LEFT:
       //move the clients player to the left

       case MOVE_UP:
       //move the clients player to the up

       case MOVE_DOWN:
       //move the clients player to the down

    }
}

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

Кроме того, лучше выполнить настройку UDP для игр, потому что простое отсутствие пакета НЕ должно мешать игровому процессу, а вместо этого должно иметь возможность обрабатывать его на стороне клиента И на стороне сервера.

person Jacob Harrison    schedule 21.06.2011