Введение


В 2017 году большинство популярных веб-игр типа agar.io использует для передачи данных WebSockets через TCP. Если бы в браузерах был встроенный UDP-аналог WebSockets, то это бы сильно улучшило работу с сетями в этих играх.

Вводная информация


Работа веб-браузеров основана на протоколе HTTP (протоколе запросов и ответов без сохранения состояния). Первоначально он был предназначен для обслуживания статичных веб-страниц. HTTP работает поверх TCP, низкоуровневого протокола, гарантирующего надёжную доставку и правильный порядок передаваемых по Интернету данных.

Всё это отлично работало многие годы, но недавно веб-сайты стали более интерактивными и перестали отвечать парадигме «запрос-ответ» протокола HTTP. Для решения этой проблемы изобретены современные веб-протоколы, такие как WebSockets, WebRTC, HTTP 2.0 и QUIC, имеющие потенциал значительного улучшения интерактивности сети.

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

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

Проблема


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

Этот принцип называется блокировкой начала очереди. Он создаёт раздражающую разработчиков и почти трагикомичную ситуацию. Самые новые данные, которые им нужны, ждут повторной пересылки старых данных, но на момент получения пересланных данных они уже устаревают и становятся бесполезными.

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

На практике это означало, что для каждой игры разрабатывался собственный протокол поверх UDP, реализующий весь необходимый функционал, и отправляющий бoльшую часть данных ненадёжным способом без сохранения порядка. Это обеспечивало максимально быструю доставку временны?х данных без ожидания повторной передачи утерянных пакетов.

А что же нужно сделать в случае с веб-играми?

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

Использование TCP совершенно необязательно, эту проблему можно было решить «по щелчку пальцев», если бы у веб-игр появилась возможность отправлять и принимать UDP-пакеты.

А что такое WebSockets?


WebSockets — это расширение протокола HTTP, модифицирующее HTTP-соединение таким образом, что данные могут передаваться в обе стороны. При этом не используется стандартный паттерн «запрос-ответ».

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

К сожалению, поскольку WebSockets реализован поверх TCP, данные всё равно подвержены блокировке начала очереди.

Что такое QUIC?


QUIC — это экспериментальный протокол, созданный поверх UDP и разработанный в качестве заменяющего транспортного слоя для HTTP. В настоящее время он поддерживается только в Google Chrome.

Важнейшая черта QUIC — поддержка множественных потоков данных. Клиент или сервер может неявным образом создавать новые каналы, увеличивая идентификатор канала (channel id).

Концепция каналов обеспечивает два больших преимущества:

  1. Позволяет избежать отправки запросов подтверждения подключения при каждом создании нового запроса.
  2. Устраняет блокировку начала очереди между несвязанными потоками данных.

К сожалению, хоть мы и устраняем проблему блокировки начала очереди у отдельных потоков, но она по-прежнему существует внутри каждого потока.

Что такое WebRTC?


WebRTC — это набор протоколов, обеспечивающих соединение типа «точка-точка» (peer-to-peer) между браузерами для таких областей применения, как потоковое воспроизведение аудио и видео.

Замечу, что WebRTC поддерживает канал данных, который можно настроить на «ненадёжный» режим, что позволяет осуществлять через браузер ненадёжную передачу данных без сохранения порядка.

Так почему же в современных браузерных играх 2017 года до сих пор используется WebSockets?

Причина заключается в том, что в многопользовательских играх существует тенденция перехода от передачи peer-to-peer к клиент-серверной модели. И хотя WebRTC позволяет удобно отправлять ненадёжные «беспорядочные» данные из браузера в браузер, он терпит крах, когда требуется передача данных между браузером и выделенным сервером.

Проблема возникает из-за чрезвычайной сложности WebRTC. Причины этой сложности понятны: WebRTC в первую очередь был разработан для обмена данными peer-to-peer между браузерами, поэтому для обхода NAT и передачи пакетов он в худшем случае требует поддержки STUN, ICE и TURN.

Но с точки зрения разработчиков игр вся эта сложность ложится на них мёртвым грузом, ведь STUN, ICE и TURN совершенно не нужны для обмена данными с выделенными серверами, имеющими публичные IP-адреса.
«Я чувствовал, что нам нужна UDP-версия WebSockets. Это единственное, о чём мы мечтали».
Матеус Валадарес (Matheus Valadares), создатель agar.io
Если вкратце, то разработчики игр любят простоту, и решение типа «WebSockets for UDP» привлекает их гораздо больше, чем сложность WebRTC.

Почему бы просто не разрешить отправлять UDP?


Последний вариант решения проблемы — просто позволить пользователям отправлять и получать UDP-пакеты непосредственно через браузер. Разумеется, это абсолютно ужасная идея и есть веские причины тому, почему этого никогда нельзя допустить.

  1. Веб-сайты смогли бы запускать DDoS-атаки, координируя массовую рассылку UDP-пакетов из браузеров.
  2. Появились бы новые дыры в безопасности, потому что JavaScript, выполняемый на веб-страницах, мог бы создавать вредоносные UDP-пакеты для «прощупывания» внутренней системы корпоративных сетей и передавать отчёты по HTTPS.
  3. UDP-пакеты не шифруются, поэтому атакующему очень просто организовать сниффинг и чтение всех данных, передаваемых в этих пакетах, или даже изменять их при передаче. Обеспечение возможности передачи браузерами незашифрованных пакетов стало бы огромным шагом назад в сетевой безопасности.
  4. В UDP отсутствует аутентификация, поэтому выделенный сервер, считывающий отправленные браузером пакеты, должен был бы применять собственный метод валидности подключающихся к нему пользователей. Такие трудозатраты гораздо выше тех усилий, которые разработчики игр готовы вложить в решение этой проблемы.

Итак, совершенно ясно, что JavaScript ни в коем случае не должен создавать UDP-пакеты в браузере.

Каким может быть решение?


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

Меня зовут Гленн Фидлер (Glenn Fiedler), я занимаюсь разработкой игр в течение последних 15 лет. Бoльшую часть этого времени я специализировался в сетевом программировании. Я получил огромный опыт, работая над динамичными экшн-играми. Последней игрой, над которой я работал, была Titanfall 2.

Около месяца я прочитал эту статью на Hacker News: WebRTC: будущее веб-игр.

В ней создатель agar.io Матеус Валадарес рассказывал, что WebRTC слишком сложен для него, и он продолжает использовать в своих играх WebSockets.

Я задумался: ведь наверняка должно быть более простое решение, чем WebRTC?

Мне стало интересно, как бы выглядело такое решение?

По моему мнению решение должно обладать следующими свойствами:

  1. Оно должно устанавливать соединение, чтобы его нельзя было использовать в DDoS-атаках и для поиска брешей в безопасности.
  2. Шифрование, потому что в 2017 году ни одна игра или приложение не должны отправлять незашифрованные пакеты.
  3. Аутентификация, потому что выделенные серверы должны принимать соединения только от клиентов, авторизованных в бекэнде.

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

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

netcode.io


Решение к которому я пришёл — это netcode.io

netcode.io — простой сетевой протокол, позволяющий клиентам безопасно подключаться к выделенным серверам и обмениваться данными по UDP. Он ориентирован на подключения, шифрует и подписывает пакеты, а также обеспечивает поддержку аутентификации, чтобы к выделенным серверам могли подключаться только авторизованные клиенты.

Он предназначен для таких игр, как agar.io, которым требуется разнести игроков с основного веб-сайта на экземпляры выделенных серверов. Каждый из серверов имеет ограничение на максимальное количество игроков (в базовой реализации — до 256 игроков на экземпляр сервера).

Основная идея заключается в том, что веб-бэкенд выполняет авторизацию. Когда игрок захочет поиграть, бекэнд осуществляет вызов REST для получения токена подключения, который передаётся выделенному серверу как часть запроса подтверждения подключения по UDP.

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

netcode.io выигрывает у WebRTC в простоте. В нём применяется схема только с выделенными серверами, поэтому использовать ICE, STUN и TURN не требуется. Благодаря реализации шифрования, подписей и аутентификации с помощью libsodium он позволяет избежать сложностей полной реализации DTLS, при этом обеспечивая тот же уровень безопасности.

За прошлый месяц я создал базовую реализацию netcode.io на C. Она выпущена под лицензией BSD из трёх пунктов. За несколько месяцев я надеюсь усовершенствовать эту реализацию, написать спецификацию и поработать с другими разработчиками над портированием netcode.io на различные языки.

Как это работает


Клиент авторизируется в веб-бекэнде с помощью стандартных техник аутентификации (например, через OAuth). После авторизации клиента он отправляет запрос на начало игры, выполняя вызов REST. Вызов REST возвращает клиенту по HTTPS токен подключения, закодированный в base64.

Токен подключения состоит из двух частей:

  1. Приватная часть, зашифрованная и подписанная общим приватным ключом с помощью примитива AEAD из libsodium. Его невозможно считать, модифицировать или подделать в клиенте.
  2. Публичная часть, предоставляющая информацию, необходимую клиенту для подключения. Например, ключи шифрования для UDP-пакетов и список адресов серверов, к которым можно подключиться, а также другую информацию, относящуюся к части «связанных данных» AEAD.

Клиент считывает токен подключения и имеет список N IP-адресов, к которым можно подключиться. Поскольку N может быть равным 1, лучше всего передавать клиенту адреса нескольких серверов на случай, если ко времени попытки подключения клиента первый сервер будет уже заполнен.

При подключении к выделенному серверу клиент периодически отправляет по UDP пакет запроса на подключение. Этот пакет содержит приватные данные токена подключения, а также дополнительные данные для AEAD, например, информацию о версии netcode.io, идентификатор протокола (64-битное число, уникальное для каждой конкретной игры), временну?ю метку срока действия токена подключения и порядковый номер примитива AEAD.

Когда выделенный сервер получает по UDP запрос на подключение, он сначала с помощью примитива AEAD проверяет валидность содержимого пакета. Если какие-то публичные данные в пакете запроса на подключение были изменены, то проверка сигнатуры выдаст ошибку. Это не позволит клиентам изменять временну?ю метку срока действия токена подключения, а также позволит быстро отклонять токены с истёкшим сроком.

Если токен подключения валиден, то он расшифровывается. Внутри он содержит список адресов выделенных серверов, для которых он валиден. Это не позволяет вредоносным клиентам использовать один токен для подключения ко всем доступным серверам.

Сервер также проверяет, не был ли токен подключения уже использован, выполняя поиск краткой истории HMAC токена. Если совпадение найдено, то запрос на подключение игнорируется. Благодаря этому один токен нельзя использовать для подключения нескольких клиентов.

Кроме того, сервер допускает подключение только одного клиента с одним IP-адресом и портом в любой момент времени. Также одновременно к серверу может быть подключен только один клиент по уникальному client id. Идентификатор client id — это 64-битное целое число, уникальным образом идентифицирующее клиента, авторизованного веб-бекэндом.

Если срок действия токена подключения не истёк, он дешифруется. Если публичный IP-адрес выделенного сервера находится в списке адресов серверов и все остальные проверки выполнены успешно, то выделенный сервер устанавливает соответствие между IP-адресом клиента и ключами шифрования, содержащимися в приватных данных токена подключения.

С этого момента все пакеты, передаваемые между клиентом и сервером, шифруются этими ключами. Если в течение короткого промежутка времени (например, пяти секунд) UDP-пакеты от адреса не поступают, то связка адреса и ключей шифрования становится недействительной.

Затем сервер проверяет, есть ли на сервере место для клиента. Каждый сервер поддерживает определённый максимум клиентов. Например, в игре на 64 игроков будет 64 места для подключения клиентов. Если сервер заполнен, он отвечает пакетом отказа на запрос подключения. Это позволяет клиентам быстро узнавать о том, что сервер заполнен и нужно переместиться на следующий сервер в списке.

Если на сервере есть место для клиента, то сервер не предоставляет это место сразу. Вместо этого он хранит адрес + HMAC токена подключения клиента как потенциального клиента. Затем сервер отвечает пакетом вызова подключения, содержащим токен вызова. Токен вызова — это блок данных, зашифрованных случайным ключом. Ключ выпускается в момент запуска сервера.

Рандомизация ключа гарантирует отсутствие проблем с безопасностью, возникающих при шифровании токенов вызова нескольких серверов одним порядковым числом (серверы не координируются друг с другом). Кроме того, пакет вызова подключения значительно меньше пакета запроса на подключение, что позволяет избежать использования протокола для DDoS-атак типа «усиление».

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

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

В противном случае сервер назначает клиенту свободное место на сервере и отвечает пакетом поддержки подключения, который сообщает клиенту, что ему выделено место на сервере. Такое место называется индексом клиента. В многопользовательских играх он обычно используется для идентификации клиентов, подключённых к серверу. Например, клиенты 0, 1, 2, 3 в игре на четырёх игроков соответствуют игрокам 1, 2, 3 и 4.

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

Флаг подтверждения для каждого клиента изначально имеет значение false и становится true, когда сервер получает от клиента пакет поддержки подключения или пакет полезной нагрузки. Пока клиент не подтверждён, при каждой отправке пакета полезной нагрузки этому клиенту предварительно отправляется и пакет поддержки соединения. Это гарантирует статистическую вероятность того, что клиент знает свой индекс и будет полностью подключён до получения первого пакета полезной нагрузки, что минимизирует количество циклов установки подключения.

После того, как подключение клиента и сервера полностью выполнено, они могут обмениваться UDP-пакетами в обоих направлениях. Обычно игровые протоколы отправляют введённую игроком информацию от клиента к серверу с большой скоростью, например, 60 раз в секунду, а состояние мира от сервера к клиенту немного реже, например, 20 раз в секунду. Однако в самых современных AAA-играх скорость обновления данных сервера увеличена.

Если сервер или клиент не передаёт стабильный поток пакетов, то автоматически генерируются пакеты поддержки подключения, чтобы подключение не прервалось по тайм-ауту. Если в течение короткого промежутка времени, например, пяти секунд, с обеих сторон не получено ни одного пакета, подключение обрывается по тайм-ауту.

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

Заключение


В популярных веб-играх типа agar.io передача данных осуществляется через WebSockets поверх TCP, поскольку в контексте клиент-серверной структуры с выделенными серверами WebRTC использовать сложно.

Один из вариантов решений для Google — сделать интеграцию поддержки каналов данных WebRTC для выделенных серверов гораздо более простой для разработчиков игр.

Или же можно использовать netcode.io, применяющий намного более простое решение типа «WebSockets для UDP». Если стандартизировать его и встроить в браузеры, это тоже может решить проблему.



Гленн Фидлер (Glenn Fiedler) — основатель и президент The Network Protocol Company. Он предоставляет услуги по настройке сетевой части игр. До основания компании Гленн был ведущим программистом Respawn Entertainment, где работал над Titanfall 1 и 2.

Гленн также является автором нескольких популярных циклов статей на gafferongames.com о сетевой передаче данных и физике в играх. Фидлер создал сетевые библиотеки libyojimbo и netcode.io с открытым исходным кодом.
Поделиться с друзьями
-->

Комментарии (55)


  1. Togran
    28.02.2017 13:41
    +11

    UDP-пакеты не шифруются

    В UDP отсутствует аутентификация

    Может, я чего-то не понимаю, но разве шифрование и аутентификация есть в TCP?


    1. PatientZero
      28.02.2017 14:10
      +2

      Я так понял, что подразумевается наличие стандартных решений с шифрованием поверх TCP (HTTPS) и отсутствие таких решений в UDP.


      1. ValdikSS
        28.02.2017 15:02
        +5

        Такие решения есть — DTLS.


    1. SvyatoslavVilkovych
      28.02.2017 14:12

      Механизм TCP предоставляет поток данных с предварительной установкой соединения, осуществляет повторный запрос данных в случае потери данных и устраняет дублирование при получении двух копий одного пакета, гарантируя тем самым, в отличие от UDP, целостность передаваемых данных и уведомление отправителя о результатах передачи. (Wiki)


      Думаю дело как раз в гарантии целостности данных.


    1. oPOCCOMAXAo
      28.02.2017 14:14

      SSL ведь используется для этой цели в TCP


    1. j_wayne
      28.02.2017 15:03

      Причем UDP можно защитить DTLS (Datagram Transport Layer Security): https://tools.ietf.org/html/rfc4347


  1. Revertis
    28.02.2017 14:20
    +7

    Вообще непонятен момент как он перешел от фазы «браузеры не умеют и не должны уметь UDP» к фазе «Вот вам протокол, тут есть UDP и мы можем на нем писать игры».
    Это некий плагин к браузеру, или что?
    П.С.: На гитхабе тоже об этом умалчивается. Везде формулировка типа «netcode.io это протокол».


    1. Tatikoma
      28.02.2017 14:38
      +4

      Так он вроде нигде не говорит, что это работает в браузерах. «Это протокол».

      Непонятно что ему мешало сделать прозрачную прослойку поверх WebRTC.

      IMHO, не выстрелит, т.к. нельзя легко пощупать.


  1. doom369
    28.02.2017 14:43
    +2

    Как-то многовато рекламы агарио для одного поста.


  1. Vindicar
    28.02.2017 14:59
    -3

    Т.е. сначала создаём себе трудности — пытаемся писать игры в браузере — а потом их героически преодолеваем.
    Может, не стоит забивать гвозди пассатижами?


    1. doom369
      28.02.2017 15:02

      Ставить клиент — это автоматически отсечь половину пользователей.


      1. raveclassic
        28.02.2017 15:06

        Ну вот, сейчас набегут десктопщики, и опять все по новой.


      1. Vindicar
        01.03.2017 10:26

        Да я понимаю, что юзверям надо «всё, сразу, бесплатно и без регистрации». Более того, сам пользуюсь веб-клиентами к некоторым IM-сервисам. Но всё равно какое-то чувство неправильности происходящего остаётся.
        Как у Лема в его истории про всё более и более навороченные стиральные машины, которые в конце своей «эволюции» уже и стирать-то не могли (и не хотели).


  1. roboter
    28.02.2017 15:06
    +7

    Итак, совершенно ясно, что JavaScript ни в коем случае не должен создавать UDP-пакеты в браузере

    честно говоря не убедили, шифрование и аутентификация не всегда нужны, DDoS можно ограничить соединение лишь со своими доменами, прощупывать можно и с TCP.


    1. Tatikoma
      28.02.2017 15:29
      +11

      Я бы даже сказал что совсем не убедили.

      В том же Adobe Flash есть некий DatagramSocket, который, согласно мануалу, — умеет:
      >>The DatagramSocket class enables code to send and receive Universal Datagram Protocol (UDP) packets.

      Флеш конечно та еще дырища, однако историй DDoS с помощью этого функционала я припомнить не могу.

      Удивительно почему автор, считающий себя таким большим специалистом, — не упомянул про это.


      1. yaroslav-korotaev
        03.03.2017 14:57

        DatagramSocket это только для AIR, во Flash Player он не работает.


  1. wlbm_onizuka
    28.02.2017 23:32
    -1

    Проблема с TCP надуманна и преувеличена.
    в случае плохого соединения нормально поиграть не получится независимо от протокола
    а в случае хорошего соединения
    1) 60-100мс пинг до сервера
    2) ~200мс лаг человеческого восприятия
    3) <1мс какая-то там разница между TCP и UDP

    скажите мне что я не учел?
    да кстати что это за дурацкий принцип, который они называют «блокировкой начала очереди», мы же ведь не собираемся отправлять пакеты в бесконечном цикле, забивая весь канал, а потом жаловаться что что-то пошло не так. или собираемся? и будем жаловаться на TCP?


    1. Blooderst
      01.03.2017 00:21
      +2

      Представим игру, в которой сервер раз в 20 секунд отправляет клиенту по TCP координаты игрока (интервал в 50 миллисекунд). Допустим, что средний пинг конкретного соединения между клиентом и сервером равен 100 миллисекундам, чего более чем достаточно для комфортной игры. Но вдруг один из пакетов дропнулся. В этом случае сервер подождет некоторое время SRTT(Smoothed Round Trip Time), которое равно примерно среднему пингу, т.е. 100 миллисекунд и потом отправит этот пакет повторно, который будет идти до клиента еще 50 миллисекунд.

      При этом все остальные пакеты (в данном случае 150/50=3 штуки) были доставлены вовремя, но «протухли» в буфере клиента, поскольку перестали быть актуальными. Вот такая ситуация и называется блокировкой начала очереди.

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

      А в случае передачи по UDP сервер ничего не передает повторно, но и клиент ничего не ждет. И в данной ситуации фриз будет всего 50 миллисекунд вместо 150.


      1. wlbm_onizuka
        01.03.2017 08:28
        -2

        во первых отправлять пакеты 20 раз в секунду неправильно. о чем я и говорил. отправлять пакеты надо по мере необходимости, когда есть новые данные.
        во вторых TCP пакет на нормальном соединении просто так не дропнется. если у вас дропаются TCP пакеты, что-то сильно пошло не так, и никакое UDP уже не поможет
        в третьих, TCP это транспортный протокол, который сам решает транспортные проблемы. если что-то там внутри него и дропается, он исправляет проблему, и делает это очень быстро. все что вы сможете заметить — небольшой скачек пинга.


        1. Blooderst
          01.03.2017 10:55
          +1

          1) В онлайн-игре каждый тик симуляции меняет игровую ситуацию. Как правило, частота передачи пакетов игрового состояния равняется частоте тиков симуляции. Так что новые данные есть всегда.

          2) TCP пакет на нормальном соединении может в любой момент быть дропнут шейпером провайдера, когда канал полностью загружен. Это обычное и регулярное явление, которое вы даже не замечаете, пока не начинаете использовать сверх-интерактивные приложения (а именно такими являются сетевые многопользовательские игры) поверх TCP. Полностью загрузите свой канал и попингуйте ya.ru, чтобы убедиться в этом.

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

          3) Все верно, TCP решает все транспортные проблемы. Но что значит «очень быстро»? Как я показал, TCP решает проблему как минимум за время пинга. Если этот пакет несет в себе часть картинги в браузере, то это не критично, но даже в этом случае это не очень быстро, а достаточно быстро. Если же это кадр тика симуляции, то увеличение срока его доставки минимум в полтора раза — это очень медленно.


          1. wlbm_onizuka
            01.03.2017 11:33
            -1

            1) Пишем свой небольшой тестовый клиент и сервер, сервер запускаем на VDS, пинг до которого примерно 100мс.
            2) отправляем несколько тысяч tcp пакетов, замеряем средний пинг
            3) отправляем несколько тысяч udp пакетов, замеряем средний пинг. потерянные пакеты выбрасываем
            4) думаете разница будет хоть сколько-то значительна?))


            1. Blooderst
              01.03.2017 11:52

              Нам не нужно мерить пинг, нам нужно мерить время доставки пакетов — это разные вещи.

              Тогда в случае плохого коннекта на TCP время доставки будет выше, чем на UDP.

              В случае идеального коннекта ни какой разницы не будет.


              1. Blooderst
                01.03.2017 12:03

                При идеальном коннекте средняя скорость доставки пакета по обоим протоколам будет равна половине среднего пинга.

                Когда коннект не идеален, в обоих протоколах скорость доставки увеличится, но при TCP увеличится на большее значение.

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


              1. wlbm_onizuka
                01.03.2017 12:04

                время доставки пакета это и есть пинг
                о том и речь что в случае идеального коннекта разницы не будет
                а когда коннект не идеальный — вы не будете играть

                когда говорят что UDP «быстрый» имеется ввиду уменьшение накладных расходов, что выгодно при передаче жирного трафика вроде потокового видео. Или в том случае если у вас реально нагруженный сервер, например игра вроде League of Legends.

                на стороне игрока разница не будет заметна. Я лично на своей первой работе играл в лигу легенд через прокси сервер, прокинув http-туннель до дома, и не заметил ни малейшего дискомфорта.

                если уж такая вакханалия как UDP over HTTP прекрасно работает, чего уж там говорить о разнице UDP vs TCP


                1. Blooderst
                  01.03.2017 12:40
                  +1

                  Нет, время доставки пакета — это сколько пакет шел в реальности, а пинг в данном контексте — это средняя скорость с которой пакет идет туда-обратно. Другими словами пинг можно считать некоей константой конкретного коннекта, идеальным временем доставки. В условиях интернета время доставки пакетов не всегда будет идеальным, т.е. не всегда будет равно половине пинга.

                  Проблема TCP в том, что когда один из пакетов теряется, пришедшие за ним пакеты оседают в буфере сетевого драйвера и не доступны программе до тех пор, пока потерявшийся пакет не будет доставлен повторно. Это явление и называется очередью блокировок. Пакет к вам доставлен, но вам не доступен. Это явление реально, оно существует и является проблемой. В случае передачи данных, актуальность которых теряется за время, меньшее пинга, при потере 1 пакета вы автоматически теряете еще несколько благополучно пришедших в довесок из-за их протухания на расстоянии вытянутой руки от вашей программы.

                  Я не понимаю, почему вы так упорно доказываете мне что проблемы не существует, когда она есть, когда она изучена и ясна для любого, кто более-менее углубился в понимание протоколов tcp и udp.

                  Откройте любую сетевую игру, просниферьте трафик и вы увидите, что такие игры открывают всегда минимум по 2 коннекта — udp для передачи скоропортящихся данных (игровое состояние) и tcp для передачи остальных данных (чат и прочее). И никакие накладные расходы и нагруженность тут ни при чем. Единственная причина — устранение очереди блокировок.


                  1. Blooderst
                    01.03.2017 12:46

                    Можно без снифера провести простой тест — заблокируйте на роутере или на компе в файрволе весь udp трафик и попробуйте запустить какую-нибудь сетевую игру, мобу или мморпг и увидите, что ни одна из них не запустится, поскольку они все используют udp. Вы думайте, разработчики игр настолько тупые, что всегда используют два протокола вместо одного, раз tcp так хорош?


                    1. wlbm_onizuka
                      01.03.2017 13:00
                      -1

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

                      Я знаю что игры создают 2 коннекта, и утверждаю что это только для борьбы с накладными расходами. в подтверждение чего привожу пример, что можно играть через UDP over HTTP и не будет никакой заметной разницы.

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

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

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


                      1. Blooderst
                        01.03.2017 13:17

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


                      1. KyHTEP
                        03.03.2017 14:57

                        Как мне показалось, вы неправильно понимаете зачем нужен UDP.

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

                        В TCP вам ГАРАНТИРОВАННО придет и старое добро и новое, это и есть задержка, с которой справляются с помощью UDP.

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

                        Для этого и создают два соединения.

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

                        PS.: А то, что у вас пробросился хорошо UDP по HTTP ни о чем не говорит, кроме как о том, что вы близко к серверу и у вас отличный канал, а так же о том, что к вам UDP пакеты прилетают пачками, обернутыми в TCP.


  1. Neuyazvimy1
    01.03.2017 11:35

    Сомнительная статья. Что udp, что tcp были разработаны со своими целями. Лучше использовать их вместе (в вебе пока хватает tcp с головой). А насчет шифрования это самое последнее о чем бы я подумал (уж очень часто автор упоминал шифрование).


    1. Neuyazvimy1
      01.03.2017 11:47

      К тому же NodeJs нативно асинхронен (Non-blocking IO). Далее не буду объяснять что да как.


      1. Blooderst
        01.03.2017 11:56

        Нет, все-же объясните, как нативная асинхронность node.js может изменить фундаментальные принципы работы протокола tcp.


    1. Blooderst
      01.03.2017 11:55

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


  1. Blooderst
    01.03.2017 18:07

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

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


    1. oxidmod
      01.03.2017 18:31

      Он избыточен для браузерок по типу agar.io, а чтото более серьезное будет клиентским


      1. Blooderst
        01.03.2017 18:56

        Спорное утверждение. Игры во всю стремятся в браузеры, их качество растет, их бюджет растет и эта тенденция в обозримом будущем вряд ли прекратится, потому что браузер — это очень доступная и распространенная платформа для игр. Не стоит считать браузерки второсортным продуктом, посмотрите, например, на hordes.io. Избыточен ли для этой игры хороший коннект? Это инди игрушка. Будет ли избыточен хороший коннект для подобной игры, выпущенной большим игроком с приличным бюджетом? Я думаю, в обоих случаях ответ — нет, но во втором он более очевиден.


        1. oxidmod
          01.03.2017 20:50

          Вы можете себе представить 2-3к игроков на осадена Руны в браузере? Для серьезных игр онли клиент. Для остальных сойдет и браузер


          1. Blooderst
            01.03.2017 21:37

            Ок, 2-3к игроков. Нагрузку на сервер мы в расчет не берем, потому что серверу все равно какие у него клиенты.

            Значит вы имеете в виду, что браузер не сможет отобразить/рассчитать одновременно 2-3к игроков? Да, не сможет. И клиент не сможет. Ни разу не видел игры с таким огромным количеством участников в одном месте. Не видел потому, что игроделы уже давным-давно поняли, что собирать толпу народа в ограниченном пространстве совсем не круто по целому ряду причин (нагрузка на сервер, нагрузка на сеть, нагрузка на видеокарту/процессор клиента, нагрузка на глаза и мозг игрока), поэтому придуманы различные инстансы, рейды, батлграунды, фаззинг и прочее. Одним словом, размазывание игроков по миру и сбор их в удобные группы — это вопрос геймдизайна, правила что для десктопного приложения, что для браузера совершенно одинаковы.


            1. oxidmod
              01.03.2017 22:32

              Зайдите на любой офф-сервер Lineage2, который запущен меньше года назад. Там и не такие толпы на осады собираются. И да… это в одной относительно компактной зоне вокруг замка, а в целом в практически бесшовном мире (не берем в учет инсты и локации в которые кроме телепорта никак не попасть) может быть гораздо больше.


              1. Blooderst
                01.03.2017 22:50

                Мне почему-то кажется что браузер легко потянет графику линейки ))) Но, даже, допустим, что не потянет, но процент таких игр очень мал. Подавляющее большинство популярных игр толпы в одном месте не собирает. А общее количество игроков в игровом мире ни на что кроме сервера не влияет. Сколько бы не было всего на сервере игроков, клиент обрабатывает и отрисовывает лишь тех, которые находятся в его зоне видимости, а об остальных он ничего не знает и знать не должен.


                1. oxidmod
                  01.03.2017 22:57

                  А вы бы попробовали сначала, а потом говорили.

                  зы. да и на таком количестве игроков пробелма с блокирвокой пакетов в очереди чувствуется ой как хорошо


                  1. wlbm_onizuka
                    02.03.2017 09:16

                    Lineage клиент однопоточный и тормозит в нем графика, а не сеть.


                    1. oxidmod
                      02.03.2017 10:13

                      На нормальной машине графика там не тормозит, тем более на осадах все красивости вырубаются в настройках. Я как раз о сети. В браузере не сделаешь такую работу с сетью как позволяет сделать клиент. Браузерка не вытянет передачи инфы о таком количестве игроков без тормозов со стороны сети.
                      Только UDP спасет отца русской демократии)


                      1. wlbm_onizuka
                        03.03.2017 08:00

                        Ну что вы говорите, там в настройках можно установить предел количества отображаемых персонажей, и все тормоза на осаде тут-же прекращаются. Хотя все пакеты клиент конечно-же получает.
                        с точки зрения графики, там тормоза квадратично нарастают, так что клиент Lineage до сих пор нагибает даже современные компы (особенно с учетом того что видеокарта почти не задействуется, и работает все через 1 поток на CPU)


                        1. oxidmod
                          03.03.2017 09:07

                          еще раз, я о работе с сетью. Клиент работает через udp. При использовании tcp играть было бы вообще невозможно


                          1. wlbm_onizuka
                            03.03.2017 13:41

                            перед тем как говорить неправду, можно сначала проверить. или заглянуть в оффициальный сапорт Ports-necessary-to-connect-and-play-Lineage-II


                            1. oxidmod
                              03.03.2017 13:50

                              Не буду спорить, возможно чтото изменилось. В универе открывали эти порты и для udp


                              1. wlbm_onizuka
                                03.03.2017 13:56

                                они используют старую глубоко-модифицированную версию Unreal Engine, с точки зрения движка там за почти уже 15 лет ничего значимого не поменялось


    1. wlbm_onizuka
      02.03.2017 09:34

      Вот это жесть полная будет. Столько накладных расходов и столько усложнения кода… а самое главное — обмануть физику все равно не удастся.


      1. Blooderst
        02.03.2017 15:32

        Вы считаете, что сеть либо хорошо работает, либо совсем не работает. Но это не правильная точка зрения — на самом деле реальное сетевое взаимодействие за пределами локальной сети практически всегда имеет те или иные проблемы, начиная от незначительных, которые вообще не заметны и заканчивая серьезными сетевыми проблемами. Но даже в случае серьезных проблем сеть не обязательно ложиться, она работает, просто не устойчиво — эта особенность была изначально заложена и, более того, была основным критерием при создании стека tcp/ip. И да, в случае серьезных проблем в данной задаче вряд ли получится избавиться от диких лагов, но мы тут ведем речь в основном про малые и средние проблемы, с которыми можно жить и которые можно побеждать.


        1. wlbm_onizuka
          03.03.2017 08:15

          Вовсе нет. Я считаю лишь то, что транспортные протоколы TCP и UDP реализованны оптимальным образом. а разница между ними именно для пользователя приложения — минимальная. Чем стоит озаботиться среднему разработчику — так это лаг-коменсацией. А вот если сделать то что вы предложили, поднять кучу коннектов, дублировать пакеты и все такое, станет только хуже.


          1. Blooderst
            03.03.2017 12:19

            Так разве кто-то утверждает, что TCP или UDP плохие протоколы? Они хорошие, если их использовать в надлежащих случаях. Проблема в том, что UDP нельзя использовать в браузере, хотя подходящие для него случаи есть.

            Лаг-компенсация — это очень хорошо и приличные игры без нее не обходятся. Но не нужно смешивать 2 задачи в одну. Одна задача — уменьшить количество лагов. Вторая задача — сгладить последствия лагов. Эти две задачи не взаимозаменяемые, они взаимодополняющие.

            Я предложил в первую очередь разделить трафик по нескольким коннектам, чтобы уменьшить (убрать) очередь блокировок. Дублирование — это вторично и опционально.


  1. wlbm_onizuka
    03.03.2017 08:14

    del


  1. mickvav
    03.03.2017 08:24

    Кто мешает DDoS-ить левые ip-ники «запросами на подключение» по UDP? Как браузер (не javascript, а именно браузер) поймёт, что к данному ip-нику больше уже не надо слать эти пакеты?

    Тут, imho, есть отличное место для использования обратного DNS.
    Идея примерно такая — ПЕРЕД тем, как слать UDP на IP a.b.c.d, от имени сайта www.example.com браузер должен сделать запрос на, например, TXT поле d.c.b.a.in-addr.arpa, и если там таки www.example.com (или какой более умный синтаксис разбора — см. SPV, кажется, записи для почты) — то значит, таки владелец a.b.c.d не возражает, чтобы его с www.example.com мучали.
    Причем, это надо по хорошему делать RFC и реализовывать в браузере, а не отдавать на откуп прикладному уровню. Иначе, получив возможность запустить js в каком-нибудь баннере на достаточно широкой аудитории можно кого угодно за DDoS-ить чисто запросами на подключение.


  1. acupofspirt
    03.03.2017 12:40

    Получается что автор статьи по-сути придумал вебсокеты над UDP?
    А agar.io так часто упоминается скорее всего из-за того что добрая половина всех браузерных игр использует похожий характер сетевых взаимодействий и agar выбрана как «среднестатистическая» браузерка для примера.