UDP против TCP, или Будущее сетевого стека

Что мы знаем об IP-сетях

Внутри черного ящика есть уровни:

tcp-vs-udp-01

Давайте сравним TCP и UDP. В них сильно отличается структура пакетов:

tcp-vs-udp-02

Главное: TCP задуман как протокол надёжной доставки данных, а UDP — нет. Может ли оказаться, что UDP лучше решит задачу надёжной доставки?

Цели на сегодня

Теорию и математику обсуждать не будем. Будем разбирать практические кейсы.

Мобильный мир победил

Важно: сначала появились проводные сети, но беспроводные, которые появились позже, сейчас явно победили. Большая доля трафика — мобильный трафик или хотя бы вайфай.

В беспроводных сетях бывают потери пакетов, смена порядка и jitter. Протокол TCP/IP скрывает от нас эти ошибки.

tcp-vs-udp-03

Вот средние параметры соединения у пользователей мобильного интернета.

tcp-vs-udp-04

Итоги:

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

Получается, что TCP не очень эффективен для доставки контента.

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

Ошибки уменьшают утилизацию канала, а распараллеливание при ошибках помогает увеличить утилизацию:

tcp-vs-udp-05

Почему нам не подходит TCP

Итого: зачем же нужно сравнивать ТCP?

Что с этим делать?

получаем smUDP: self-made UDP

У разного контента разные профили потребления сети.

tcp-vs-udp-06

HTTP 1.1 и HTTP/2

Конечно же мы должны сравнить протоколы на HTTP 1.1 и HTTP/2.

HTTP 1.1 предлагает использовать по одному соединению на каждую единицу контента. HTTP/2 — одно мультиплексированное соединение.

tcp-vs-udp-07

При этом в HTTP 1.1 клиент (браузер) обычно использует пул соединений. Проблема в том, что между соединениями есть конкуренция. Картинка, которую пользователь уже пролистал и больше не увидит, конкурирует с другой, которая впереди в ленте. И в HTTP 1.1 сложно отменить загрузку — только закрыть сокет и отменить соединение.

tcp-vs-udp-08

HTTP/2 лучше:

Приоритизация позволяет получить приоритетный контент раньше:

tcp-vs-udp-09

Server push: сервер может отдать контент, который точно понадобится в будущем.

tcp-vs-udp-10

Отмена загрузки: если клиенту уже не понадобится контент, клиент может отказаться от загрузки.

tcp-vs-udp-11

Сравниваем TCP и smUDP

Итого, на чём нам сравнивать TCP и smUDP

Простая сеть: bandwidth + RTT

В TCP важен размер буфера отправки. Сервер держит контент в буфере, пока не получит подтверждение (acknowledgement), что контент получен. Но чем больше RTT, тем дольше ждать подтверждения.

tcp-vs-udp-12

Если мы увеличим размер буфера, то фактическая ширина канала вырастет.

tcp-vs-udp-13

Но всё не так просто. Важны on-the-fly packets. Это те, которые мы отправили, но ещё не получили подтверждения. Если буфер слишком мал, то мы недоиспользуем сеть, как мы уже поняли. Но если буфер слишком большой, то мы приходим к распуханию буфера (bufferbloat). Буфер заполнен кучей on-the-fly пакетов, а скорость снова маленькая.

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

А если у нас свой протокол, то мы можем:

tcp-vs-udp-14

Как это делается? Присваиваем отправляемым пакетам сквозной sequence number.

Итак:

Сложная сеть: bandwidth + RTT + packet loss

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

Разберёмся, как работает Congestion Control.

Для начала, TCP window: количество одновременно отправляемых пакетов. Отправитель начинает с 10 и разгоняется, увеличивая количество. Если в какой-то момент пакеты теряются, он уменьшает окно и снова разгоняется.

tcp-vs-udp-15

Congestion Control придуман для предотвращения перегрузки сети. Вот где-то в сети есть роутер, который больше всего нагружен. Он умный: не ждёт когда совсем перегрузится, а начинает дропать пакеты чуть раньше, чтобы отправитель уменьшил окно.

tcp-vs-udp-16

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

Получается, есть два типа потерь:

Каким бывает Congestion Control

Congestion Control эволюционирует. Нам особенно интересны реализации Cubic и BBR.

tcp-vs-udp-17

Если скорсть растёт, BBR схлопывает окно заранее, Cubic дожидается потери пакетов и тогда схлопывает окно.

tcp-vs-udp-18

Работают они так:

Совсем сложная сеть: bandwidth + RTT + packet loss + jitter

Казалось бы, BBR решит все наши проблемы. Но есть ещё и jitter! Он влияет в том числе на получение acknowledgements от получателя. То есть в некоторых случаях пакет доставляется успешно, но отправитель не успевает получить подтверждение и шлёт пакет снова. BBR уязвим к высокому jitter.

Было бы здорово, если бы сервер мог знать jitter клиента. Но в стандартном пакете acknowledgement (ACK Frame) в TCP этой информации нет. Зато мы можем добавить её в smUDP ACK Frame.

tcp-vs-udp-20

Мобильные сети асимметричны. Обычно 70% на приём и 30% на отправку. Стоит разделить jitter на приём и отправку.

Какой congestion control выбрать на сервере?

tcp-vs-udp-21

А на клиенте всегда Cubic и мы не можем на это повлиять.

Выводы такие:

tcp-vs-udp-22