Технология в деталях №3, апрель 2016

Транспортные протоколы

Фото аватара
Джефф Хьюстон

Хотя работа Интернета на низком уровне во многом происходит посегментно, так сказать hop-by-hop, работу основных приложений осуществляют два сквозных транспортных протокола: User Datagram Protocol (UDP) и Transmission Control Protocol (TCP). Джефф Хьюстон раскрывает в этой статье принципы работы и эволюцию этих протоколов. Особое внимание он уделяет протоколу TCP.

Удивительно, что этот протокол способен обеспечить эффективное обслуживание вне зависимости от задействованной скорости – десятков бит в секунду или миллиардов бит в секунду. Кроме того, удивительно то, что TCP остается эффективным вне зависимости от того, сколько «разговоров» (сеансов) проходит по одному каналу – является ли он единственным или одним из миллионов параллельных «разговоров» TCP. Но что на самом деле удивительно, так это сама технология, сегодня используемая в миллиардах устройств, которая остается гибкой и адаптируемой. И мы все еще можем сделать ее лучше.

Одним из ранних усовершенствований модели интернет-протокола стало отделение изначального протокола Интернета от единой монолитной спецификации и разделение на Internet Protocol (IP) и на пару транспортных протоколов. Уровень IP предназначен для использования внутренними коммутаторами внутри сети для передачи пакетов в указанное для них место назначения, тогда как уровень транспортного протокола используется между системой отправителя и получателя. В этой статье я бы хотел взглянуть на то, что происходит с этими транспортными протоколами.

В сегодняшнем Интернете обычно используются два сквозных транспортных протокола: User Datagram Protocol (UDP) и Transmission Control Protocol (TCP). Почему только два? Несомненно, мы обдумывали много способов управления потоками данных через сеть с коммутацией пакетов, и в рамках такой открытой архитектуры, как Интернет, кажется разумным спросить, почему не видно множества сквозных транспортных протоколов. В таких попытках не было недостатка, и просмотр /etc/protocols в любой Юникс-подобной системе предоставит список из 130 таких протоколов, которые находятся на уровне поверх IP. Некоторые из них представляют собой специализированные протоколы, например, протокол 89, используемый протоколами маршрутизации OSPF, а другие являются протоколами инкапсуляции, например, протокол 41 для инкапсуляции IPv6-in-IPv4. Однако в этом списке также присутствует достаточное количество сквозных протоколов, таких как протокол 27 – Reliable Datagram Protocol (RDP), протокол 132 – Stream Control Protocol (SCTP). Однако большинству этих сквозных транспортных протоколов оказалось трудно выжить в общедоступном Интернете.

Проблема сегодняшнего дня заключается в том, что промежуточные устройства взяли верх. Значительная доля Интернета укрывается за всякого рода типами промежуточного программного и аппаратного обеспечения, например, в виде брандмауэров (межсетевых экранов) и фильтров, работающих по принципу явного разрешения, а не исключения. Эти средства обычно разрешают протоколы 6 и 17 (TCP и UDP), однако в отношении разрешенных сквозных протоколов на этом все и заканчивается. Это не просто паранойя брандмауэров. У нас имеются выравниватели нагрузки, формирователи пакетов и многие другие виды перехватывающих промежуточных устройств, которые способны распознавать и управлять сеансами TCP, но обычно они больше ничего не делают. Поэтому для всех практических целей Интернет разрешает только сквозные транспортные протоколы TCP и UDP. Давайте взглянем на эти два протокола более подробно.

UDP

UDP – это очень простая абстракция базовой IP-датаграммы. И подобно самому IP, UDP является ненадежным средством. Зеркально отображая сервисные характеристики IP, пакеты, отправленные с помощью UDP, возможно, будут или не будут получены по их целевому месту назначения. Пакеты UDP могут перегруппировываться, дублироваться или теряться. В UDP не существует управления потоком данных или его регулирования. Дискретизация пакетов в UDP является явной: если данные разделены отправителем на два пакета UDP, то получатель должен принять данные с помощью двух отдельных операций чтения.

UDP предназначен для использования в чрезвычайно простых транзакциях, которые не требуют сеансового контекста. Система доменных имен (DNS, Domain Name System) и Сетевой протокол синхронизации (NTP, Network Time Protocol) являются хорошими примерами приложений, которые используют UDP для поддержки очень эффективной модели транзакций запрос/ответ. Обычно отправитель генерирует пакет запроса вместе с пространством для ответа, а ответ представляет собой тот же пакет, но с заполненным полем для ответа.

В наши дни к UDP относятся гораздо более настороженно. Отсутствие сеансового контекста означает, что большинство транзакций являются незашифрованными и не только легко «прослушиваются» третьими сторонами, но и чрезвычайно уязвимы к атакам, связанным с введением в заблуждение, при которых некто другой (а не целевой получатель) генерирует ответ с возможной целью введения в заблуждение или обмана исходного отправителя запроса. Например, в современном Интернете средства перехвата DNS стали весьма обычным способом фильтрации контента. Более зловещим выглядит то, что протокол UDP стал общей платформой для формирования различных типов DOS-атак. Многие серверы UDP, например, официальные DNS-серверы, должны отвечать на все UDP-запросы, поэтому они не могут отвечать только на определенные «подлинные» запросы. В UDP все запросы имеют обыкновение выглядеть подлинными. Эта особенность используется злоумышленниками, которые помещают IP-адрес намеченной жертвы в исходный адрес UDP-пакета. При отправлении достаточно большого количества запросов, содержащих один и тот же исходный адрес, возможно «рекрутировать» UDP-серверы, превратив их в невольных злоумышленников, атакующих жертву. Поэтому сегодня протокол UDP «впал в немилость».

Если не UDP, тогда, вероятно, TCP.

TCP

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

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

TCP не является исключением. Один из подходов заключается в том, что каждая пара коммутаторов использует надежный протокол для их общего соединения, осуществляя обнаружение и корректировку потери пакетов посегментно (hop-by-hop). TCP использует другой подход и не делает никаких допущений о надежности каждого сегмента, или хопа. Вместо этого TCP использует сквозную последовательную нумерацию данных между двумя коммуникационными системами, которая позволяет идентифицировать контекст в рамках потока данных. После получения хостом последовательно включенного пакета он отправляет обратно отправителю порядковый номер последнего байта в пакете в качестве положительного подтверждения (ACK). Это подтверждение ACK представляет собой «кумулятивный ACK» в том смысле, что оно «говорит» о получении всех данных в потоке вплоть до порядкового номера, который содержится в ACK. В TCP не существует негативных подтверждений (NACK). После прибытия пакета вне заданного порядка получатель также отправляет ACK, но содержащий порядковый номер полученного байта с самым высоким номером, который соответствует заданному порядку. Такая форма конструкции надежного протокола именуется «сквозным» управлением, в отличие от «похопового» управления, поскольку TCP не допускает, что внутренние коммутаторы попытаются исправить потерю пакетов на уровне каждого сегмента.

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

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

Но совпадают ли между собой теория и практика? Происходит ли это в современном Интернете и что меняется в этих допущениях о поведении TCP?

Управление потоками данных TCP – TCP Tahoe и TCP Reno

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

Однако такой простой подход имеет свои проблемы при его использовании несколькими параллельными сеансами, приводящие к «коллапсу сети из-за заторов». Сеансы TCP взаимодействуют таким образом, что происходит крупномасштабная потеря пакетов, а потеря сигналов ACK заставляет всех отправителей повторно передавать данные и т.д. Проведенное в 1980-х исследование этого феномена привело к введению «управления потоками» в поведение TCP.

Одним из ранних алгоритмов управления потоками данных TCP стал TCP Tahoe, впервые использованный в операционной системе 4.3BSD. В рамках этой схемы управления потоками существуют две отличительные стадии поведения: этап «Медленный старт», когда скорость отправки удваивается каждый временной интервал передачи и подтверждения (RTT, Round Trip Time), и этап «Предотвращения заторов», когда скорость отправки увеличивается на фиксированную величину (один размер сегмента сообщения (MSS, Message Segment Size)) за каждый интервал RTT.

Этап медленного старта инициирует поток данных очень консервативным способом, отправляя в сеть всего один пакет и ожидая соответствующего подтверждения ACK. Однако каждый полученный ACK приводит к тому, что отправитель удваивает размер окна отправки. В результате отправитель будет последовательно отправлять в сеть 2, 4, 8 и т.д. пакетов по истечении каждого интервала RTT. Это приводит к экспоненциальному росту скорости передачи данных в сеть, и сеанс TCP очень скоро либо достигает максимальной скорости приема удаленного получателя, максимальной начальной скорости передачи отправителя («порог медленного старта» (ssthresh (slow start threshold)), либо он столкнет сеть в затор до точки потери пакетов. В первом случае скорость потока TCP стабилизируется на максимальной скорости получателя. Во втором случае, когда скорость отправки превышает локальный порог, отправитель затем переходит в режим предотвращения заторов и продолжает увеличивать скорость отправки до тех пор, пока либо не будет достигнута максимальная скорость получателя, либо произойдет потеря пакетов.

Если подтверждение ACK для пакета не поступает в течение интервала RTT, то TCP Tahoe предполагает, что отправленный пакет был потерян в сети. Это событие потери пакета приводит к тому, что локальное значение ssthresh устанавливается равным половине текущей скорости отправки, а сама скорость отправки возвращается к одному пакету при возобновлении процесса медленного старта.

Идеализированная картина итогового поведения потока данных, управляемого протоколом TCP Tahoe, показана на рис. 1.

Рис.1. Идеализированное управление потоками данных TCP Tahoe.

Идея заключается в том, что при запуске процесса отправитель быстро зондирует скорость в направлении повышения с целью определения верхней границы устойчивой скорости передачи данных. К этому моменту отправитель знает примерную скорость. Логическая аргументация предполагает, что потеря из-за затора произошла в ходе последнего интервала RTT, и что максимальная устойчивая скорость отправки находится где-то между половиной скорости отправки последнего интервала RTT и полной скоростью отправки того же последнего интервала RTT. После этого Tahoe опять выполняет медленный старт, но останавливается на этой половинной скорости, которая, согласно результатам предыдущего этапа медленного старта, была устойчивой скоростью для сети. Отправитель входит в режим предотвращения заторов и осуществляет гораздо более осторожное зондирование с целью нахождения следующего диапазона скоростей отправки, увеличивая скорость отправки на один сегмент каждый интервал RTT. И опять при потере пакета Tahoe предполагает, что произошло пересечение границы, после чего задает ssthresh равным половине скорости отправки и перезапускает поток в режиме медленного старта.

Tahoe использует отсутствие ACK как сигнал о потере пакета в результате затора. Tahoe осуществляет внутреннюю оценку RTT и при неполучении ACK в течение интервала RTT (плюс некоторое время, выделяемое на джиттер (колебания задержки)) Tahoe осуществляет сброс в режим медленного старта. Такой ответ на потерю пакетов приводит к значительным задержкам в передаче данных, поскольку отправитель бездействует в ходе интервала ожидания, а при перезапуске процесс возобновляется с обмена одним пакетом, постепенно достигая скорости передачи, которая была до потери пакета.

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

Протокол резко реагирует на эти дублирующие сигналы ACK сетевого затора, но использует только половину предыдущей скорости отправки и затем возобновляет постепенное увеличение скорости отправки с целью достижения равновесия с параллельными сеансами TCP. Если ему не удается восстановить утерянные пакеты с помощью этого механизма быстрого восстановления, то он закрывает окно отправки и повторно входит в режим медленного старта с начальной скоростью 1. Рис. 2 показывает идеализированное поведение TCP Reno.

Ц

Рис.2. Идеализированное управление потоками данных TCP Reno

елью перехода отправителя в режим предотвращения заторов является тщательное зондирование для нахождения точки образования затора в сети с постепенным увеличением давления потока данных на сеть. В режиме предотвращения заторов дублирующие ACK заставляют отправителя вдвое снизить скорость отправки, попытаться восстановить данные из потерянного пакета и, при успешном выполнении этих задач, продолжить работу в режиме предотвращения заторов, начав с новой скорости отправки.
В устойчивом состоянии алгоритму предотвращения заторов TCP Reno удается идеально избегать перезапуска сеансов с помощью медленного старта, вместо этого он пытается продолжить поток данных с использованием того же самого допущения о вероятной потере из-за затора. Это процесс Аддитивного увеличения (Additive Increase) в рамках окна отправки (на один сегмент каждый интервал RTT) и Мультипликативного уменьшения (Multiplicative Decrease) (посредством двойного сокращения размера) при наступлении затора или сокращенно процесс AIMD.

Алгоритм AIMD протокола TCP Reno имеет обыкновение подвергать буферы сети высокому уровню давления при наличии доступного буферного пространства и менее драматично реагировать в тех случаях, когда буферы в конце концов переполняются и достигают точки отбрасывания пакетов. Управление потоками данных TCP Reno является гораздо более эффективным по сравнению с его предшественником Tahoe, однако по-прежнему существует форма управления обратной связью «бум – спад», которая пытается перевести сеть в перегрузку затора и затем отступает, обеспечивая опорожнение буферов. Невзирая на эти недостатки, Reno является главной опорой Интернета на протяжении уже более чем двух десятилетий и остается эталонным ориентиром, с которым сравниваются другие алгоритмы управления потоками TCP.

Лучше чем Reno

Существует целый ряд причин для ухода от алгоритма управления потоками данных, используемого в Reno. Один из подходов заключается в обеспечении более ровного потока пакетов через сеть и в устранении некоторого «дерганья», присущего TCP Reno. Кроме того, имеется подозрение, что более «чувствительный» механизм управления потоками сможет добиться более высокого результата, чем TCP Reno. Иными словами, другой алгоритм управления потоками данных TCP может добиться лучшего, чем «справедливая доля» при соперничестве с набором параллельных потоков TCP Reno!

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

Например, если скорость отправки увеличивается на 2 единицы MSS за каждый интервал RTT (вместо 1 единицы MSS в TCP Reno) и уменьшается на одну четверть (а не вдвое) при получении дублирующего ACK, то результирующее поведение будет, в приблизительном смысле, напоминать два параллельных сеанса TCP. В рамках сценария справедливого разделения такая форма управления потоком попытается обеспечить выделение этому сеансу двойного объема сетевых ресурсов по сравнению с эквивалентным сеансом TCP Reno.

Еще один вариант такого подхода – это «Высокоскоростной TCP», который увеличивает частоту зондирования в потенциально достижимую пропускную способность, наращивая свою скорость отправки на более крупную величину, сохраняя при этом скорость уменьшения на постоянном уровне. Поэтому вместо увеличения окна перегрузки на 1 за каждый интервал RTT (в случае Reno), высокоскоростной TCP использует расчет скорости увеличения за каждый интервал RTT, который превышает 1 по мере роста окна перегрузки.

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

BIC и его вариант CUBIC используют нелинейную функцию увеличения, а не функцию увеличения с постоянной скоростью. Вместо того, чтобы увеличивать скорость на фиксированную величину каждый интервал RTT, в режиме предотвращения заторов BIC запоминает частоту отправки на начало потери пакетов, и каждый интервал RTT увеличивает скорость на половину разницы между текущей частотой отправки и предполагаемой частотой «бутылочного горлышка». BIC быстро доводит сеанс до пропускной способности узкого места и затем осуществляет более осторожное зондирование после того, как скорость отправки приблизится к пропускной способности бутылочного горлышка (рис. 3). И опять, по сравнению с сеансом потока Reno, BIC должен выдавать превосходящий результат.

Рис.3. Идеализированное управление потоками данных TCP BIC.

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

Недавно выпущенные системы Microsoft используют протокол Compound TCP, который объединяет TCP Reno и управление потоками данных на основе задержки. Этот алгоритм пытается измерить объем данных «в полете», удерживаемый в очередях (трафик с более высокой задержкой), и, после потери пакета, алгоритм уменьшает частоту отправки до уровня, находящегося ниже начала роста в очередях согласно оценке отправителя.

Системы Apple Macintosh используют New Reno – вариант управления потоками данных Reno, который усовершенствовал процедуру восстановления потерь Reno, но в остальном остается тем же самым алгоритмом управления AIMD.
Ядра Linux переключились на использование CUBIC – варианта алгоритма BIC, который использует кубическую функцию (а не экспоненциальную) для управления расширением окна.

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

Алгоритмы управления потоками данных TCP, которые модифицируют такое поведение, имеют обыкновение лучше всего работать при использовании в среде, в которой все остальные потоки ведут себя более консервативно. В среде, в которой все параллельно работающие сеансы в рамках перегруженного канала используют AIMD-поведение подобное Reno, единственный сеанс, использующий более агрессивный ответ на потерю пакетов, например, CUBIC, обычно будет оказывать более высокое давление на параллельные Reno-подобные сеансы TCP и получит более крупную долю доступных сетевых ресурсов. Конечно, такая стратегия работает только в том случае, когда используются разнообразные алгоритмы управления потоками данных.

Скрещивая опоры: TCP в UDP

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

Такой подход много лет использовался стриминговым приложением BitTorrent (LEDBAT) и совсем недавно – компанией Google в ее экспериментах с QUIC.

Google QUIC (Quick UDP Internet Connections) использует эмуляцию TCP в UDP. QUIC эмулирует надежный протокол со скользящим окном, используемый TCP внутри потока пакетов UDP. QUIC объединяет функциональность защищенных транспортных сеансов и слотов между обычным интерфейсом API HTTP/2, в качестве интерфейса приложения, и UDP (с использованием порта 443) в качестве сквозного транспортного протокола.

QUIC реализует управление потоками данных TCP CUBIC, а также добавляет к этому алгоритму целый ряд небольших изменений.
Это включает использование Выборочного подтверждения (SACK, Selective Acknowledgement) при его активации получателем, для обнаружения случаев отбрасывания нескольких пакетов в отдельном окне RTT. Кроме того, это включает подход, направленный на разделение механизмов управления заторами TCP от действий по восстановлению данных. Цель здесь заключается в том, чтобы разрешить отправку новых данных в ходе восстановления для поддержания синхронизации TCP ACK. Этот подход, получивший название Forward Acknowledgements with Rate Halving (FACK), предусматривает отправку одного пакета для каждых двух подтверждений ACK, полученных в то время, пока TCP восстанавливается от потери пакетов. Такой алгоритм эффективно уменьшает частоту отправки наполовину в течение одного интервала RTT, но не оставляет отправителя в замороженном состоянии, ожидая половинного опустения объема окна перегрузки перед тем, как продолжить отправку данных. Обычный алгоритм восстановления заставляет отправителя прекратить отправку в течение периода длительностью до одного интервала RTT, тем самым теряя точность неявной синхронизации ACK для сеанса. FACK позволяет отправителю продолжить отправку пакетов в сеть в течение этого периода, стремясь разрешить отправителю поддержание точного ракурса синхронизации ACK. Кроме того, FACK позволяют задать количество блоков SACK, которые определяют отсутствующий сегмент, перед повторной отправкой этого сегмента, обеспечивая для отправителя более высокий уровень контроля над чувствительностью к переупорядочиванию пакетов.

Реализация также включает Tail Loss Probe (TLP) – метод, который реагирует на тайм-аут ожидания подтверждения ACK при помощи повторной отправки «хвостового» сегмента, добиваясь отклика SACK отсутствующих сегментов, которые затем можно исправить с помощью FACK. Кроме того, он поддерживает Forward Retransmission Timeout (F-RTO) для смягчения мнимых случаев, когда отправитель возвращается к медленному старту после тайм-аута ожидания ACK, и Early Retransmit для поддержки случаев, когда дублирующие ACK получены при небольшом окне перегрузки отправителя с целью предотвращения переходов к медленному старту из-за мнимого состояния.

QUIC может сделать то, что невозможно даже после множества небольших изменений TCP, а именно, он способен «играть» с некоторыми фундаментальными механизмами TCP, поскольку не существует унаследованных проблем, когда модифицированный отправитель TCP обменивается данными с обычным, не модифицированным получателем TCP. Например, QUIC использует новый порядковый номер для повторно переданных сегментов, позволяя отправителю проводить различия между ACK для исходного сегмента и ACK для повторно переданного. QUIC также всегда использует шифрование TLS и планирует принять TLS 1.3, когда эта спецификация будет завершена. Он уже внедрил «рукопожатие» с нулевым интервалом RTT.
Google намеревается продолжить развитие, используя Упреждающую коррекцию ошибок (FEC, Forward Error Correction), что позволит получателю исправлять определенные виды потоков пакетов без какой-либо повторной передачи, а также добавляя Мульти-тракт (Multipath), что позволит платформам с несколькими сетевыми интерфейсами (например, мобильным устройствам) разделять нагрузку среди всех активных интерфейсов.

QUIC выполняет оценку пропускной способности как средство для быстрого достижения эффективной скорости отправки. SPDY дополнительно помогает QUIC, осуществляя мультиплексирование нескольких сеансов приложений в рамках единого сеанса сквозного транспортного протокола. Это позволяет избежать издержек старта каждого сеанса TCP, учитывая, что TCP требуется некоторое время для определения пропускной способности узкого места коммуникационного тракта. Использование UDP также позволяет избежать промежуточных устройств, которые осуществляют перехват трафика с целью глубокого изучения пакетов в потоках TCP, и изменяет их объявленный размер окна с целью осуществления внешнего регулирования скорости потока TCP.
Определенно, это весьма интересный подход. Он избавляется от проблемы обратной совместимости, используя транспортный сеанс через UDP, поэтому в этом случае не существует подлежащих рассмотрению унаследованных ограничений TCP.

Однако существует одна проблема с использованием UDP как заменителя TCP. И хотя общедоступные отчеты Google по этой теме не были опубликованы, она остается источником беспокойства. Проблема касается использования UDP через Транслятор сетевых адресов (NAT, Network Address Translator) и времени привязки по адресу внутри NAT. В TCP Транслятор сетевых адресов получает указания от TCP.

Когда NAT «видит» начальный пакет «рукопожатия» TCP «изнутри», он создает временную привязку по адресу и отправляет этот пакет в предназначенное для него место (конечно, вместе с оттранслированным адресом источника). Получение в NAT ответной части «рукопожатия» заставляет NAT подтвердить свою запись привязки и применить ее к последующим пакетам данного потока TCP. NAT удерживает это состояние до тех пор, пока не получит сигнал закрытия обмена или сигнал сброса, который закрывает сеанс TCP, либо пока не истечет таймер простоя. Для TCP Транслятор сетевых адресов пытается удерживать привязку, пока активен сеанс TCP. С точки зрения устройств NAT, протокол UDP другой. В отличие от TCP, в UDP нет информации о статусе потока данных. Поэтому когда NAT создает привязку UDP, он должен удерживать ее в течение определенного периода времени. В данном случае не существует четкого технического стандарта, поэтому реализации могут отличаться друг от друга. Некоторые NAT используют очень короткие таймеры и быстро «отпускают» привязку, что соответствует ожиданиям об использовании UDP в качестве простого протокола для запросов/ответов. Применение UDP в качестве эрзац-протокола фреймирования пакетов для реализации TCP на уровне пользователя требует, чтобы NAT удерживал привязку по адресу UDP в течение более длительных интервалов, соответствующих скрытому сеансу TCP. Некоторые NAT это делают, тогда как другие разрушают привязку даже в том случае, когда имеются активные пакеты UDP, нарушая таким образом скрытый сеанс TCP. QUIC предполагает, что NAT будет удерживать открытой неактивную привязку UDP в течение 30 секунд. Если NAT ведет себя более агрессивно, чем это предположение, то QUIC аварийно переключится на обычный протокол TCP.

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

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

Конец сквозных протоколов?

И куда теперь?

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

Удивительно то, что протокол TCP способен обеспечить эффективное обслуживание вне зависимости от задействованной скорости – десятков бит в секунду или миллиардов бит в секунду. Кроме того, удивительно, что TCP остается эффективным вне зависимости от того, сколько «разговоров» (сеансов) проходит по одному каналу – является ли он единственным или одним из миллионов параллельных «разговоров» TCP. Но что на самом деле удивительно, так это сама технология, сегодня развернутая в миллиардах устройств и остающаяся гибкой и адаптируемой. И мы все еще можем сделать ее лучше.

Сквозная эра определенно еще не закончилась!

Источник: www.potaroo.net: Transport Protocols

Литература

[1] V. G. Cerf, R. E. Kahn, (May 1974). “A Protocol for Packet Network Intercommunication” (PDF). IEEE Transactions on Communications 22 (5): 637–648. doi:10.1109/tcom.1974.1092259. http://ece.ut.ac.ir/Classpages/F84/PrincipleofNetworkDesign/Papers/CK74.pdf
[2] J. Postel, “User Datagram protocol”, RFC 768 28 August 1980.
[3] J. Postel, “Transmission Control Protocol”, RFC794, September 1981.
[4] V. Jacobsen, M. Karels, Congestion Avoidance and Control, 1988, http://ee.lbl.gov/papers/congavoid.pdf.
[5] W. Stevens, “TCP Slow Start, Congestion Avoidance, Fast Retransmit, and Fast Recovery Algorithms,” RFC2001, January 1997.
[6] M. Allman, V. Paxon, E. Blanton, TCP Congestion Control, RFC5681, September 2009.
[7] P. Dodcal, “15 Newer TCP Implementations”, http://intronetworks.cs.luc.edu/current/html/newtcps.html.
[8] Fast: https://en.wikipedia.org/wiki/FAST_TCP.
[9] Google’s QUIC: https://www.chromium.org/quic, http://blog.chromium.org/2015/04/a-quic-update-on-googles-experimental.htm, retrieved 21 June 2015.
[10] IETF activity on TCP flow control: TCP Maintenance and Minor Extensions (tcpm) https://datatracker.ietf.org/wg/tcpm/charter/