Об UDP

Везде, где объясняют UDP, его объясняют неправильно.
Примерно так: “TCP всегда доставляет пакеты, а UDP ненадёжный протокол, и иногда может их терять”.

“К этому нужно быть готовым”, говорит объясняющий.

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

Это не просто неправильный вывод — это вообще неправильное понимание, что происходит.

Вот правильное объяснение

Вот правильное объяснение:
UDP это протокол, который разговаривает с вами, выбрасывая пакеты.

TCP следит за пропускной способностью сам. Сервер на другом конце света недостаточно быстро разбирает входящие? Рутер перед ним тянет только 10Мбит, хотя на вашей стороне гигабит? Вы махом отправили больше, чем ваша сетевая карта успевает обработать?
Во всех этих случаях TCP притормозит отправку: либо задержится в send(), либо вернёт WSAEWOULDBLOCK.

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

Слишком рано — значит, быстрее, чем пакеты передаёт самое медленное звено. Представляйте себе реку. Там, где русло узкое, вода скапливается и образует запруду. Запруда это буфер, такой, как буфер отправки. И если вода прибывает быстрее, чем уходит, запруда будет расти и расти без конца.

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

Когда UDP объяснили неправильно (“ненадёжный протокол”), само собой напрашивается: если пакет утерян, надо отослать его ещё раз. Со второй попытки не вышло, значит, с третьей. Чем больше попыток – тем больше шансов доставить!

Но всё наоборот: Если пакет утерян, надо выждать, пока наводнение не схлынет. Чем чаще потери, тем дольше надо выжидать. И только потом осторожно пробовать снова.

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

Достигнув какой-то минимальной скорости, всё стабилизируется – UDP, страдая, принимает потоки ваших пакетов, из которых большая часть ненужные дубликаты, выкидывает 90%, не влезающие в канал, и иногда полезным пакетам везёт, и они всё-таки пробиваются сквозь все перегруженные буферы, не попав под сокращение. Капля за каплей, передача продвигается. Программист делает вывод, что его рутер плохо держит нагрузку и “начинает теряет пакеты”.

Кому-то может даже придти в голову сразу отправлять все пакеты дважды или трижды, а не ждать, пока другая сторона сообщит об утере. Ведь если дело в надёжности, то три попытки очевидно надёжнее одной. Но понятно же, как это будет работать на самом деле?

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

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

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

Если ваш протокол допускает не более, чем фиксированное число “пакетов в воздухе” — например, нельзя отослать новый запрос до получения ответа на предыдущий — ограничивать скорость не обязательно, достаточно обеспечить повторы. Но если есть запросы с данными произвольного размера (отправка файлов), или запросов можно отправить сколько угодно сразу, то шейпинг нужен, иначе рано или поздно обмен будет застревать.

Напишите комментарий:

Если хотите, можно залогиниться.

*