Теперь рассмотрим методы решения всех этих проблем. Главный принцип, который каждый разработчик высокоскоростных сетей должен выучить наизусть, звучит так:
Проектируя, стремись увеличить скорость, а не оптимизировать пропускную способность.
При разработке старых протоколов обычно ставилась задача минимизировать количество битов, переданных в линию, часто за счет использования полей малого размера и упаковывания нескольких полей вместе в один байт или слово. Сегодня экономить пропускную способность смысла нет: ее более чем достаточно. Проблема заключается в обработке пакетов, поэтому при разработке новых протоколов минимизировать нужно именно время обработки. Разработчики IPv6, к счастью, хорошо понимают это.
Возникает соблазн создать быстрый сетевой интерфейс на аппаратном уровне. Сложность в том, что если протокол не является совсем простым, аппаратный интерфейс представляет собой дополнительную плату с отдельным процессором и своей программой. Чтобы сетевой сопроцессор не был столь же дорогим, как и центральный CPU, он часто представляет собой медленную микросхему. В результате центральный CPU ждет, пока сопроцессор выполнит свою работу. Было бы ошибкой полагать, что у центрального CPU обязательно найдется другая задача на время ожидания. Кроме того, для общения двух процессоров общего назначения потребуются тщательно продуманные протоколы, обеспечивающие их корректную синхронизацию. Как правило, наилучший метод заключается в создании простых протоколов, чтобы всю работу мог выполнять центральный процессор.
Для гигабитных сетей крайне важна структура пакета. В заголовок следует включить как можно меньше полей — это позволит снизить время его обработки. Сами поля должны быть достаточно большими, чтобы справляться с задачей, при этом они не должны пересекать границы слов; тогда их будет проще обработать. Под «достаточно большим» подразумевается размер, который исключает зацикливание порядковых номеров при существовании в сети старых пакетов, неспособность получателя сообщить доступный размер окна из-за мелкого служебного поля и другие проблемы.
Максимальный объем данных должен быть большим, чтобы снизить программные накладные расходы и обеспечить эффективную работу сети. 1500 байт — недостаточный размер пакета для высокоскоростных сетей, поэтому гигабитная сеть Ethernet позволяет передавать сверхдлинные фреймы размером до 9 Кбайт, а IPv6 поддерживает джамбограммы, превышающие 64 Кбайт.
Далее мы обсудим вопрос обратной связи в высокоскоростных протоколах. Из-за относительно длинного цикла задержки желательно отказаться от обратной связи: на оповещение отправителя уходит слишком много времени. Пример обратной связи — управление скоростью передачи с помощью протокола раздвижного окна. Чтобы избежать задержек (которые могут быть долгими), связанных с передачей сведений о состоянии окна от получателя отправителю, лучше использовать протокол, основанный на скорости. В этом случае отправитель будет передавать данные на той скорости, с которой получатель заранее согласился (но не быстрее).
Еще один пример обратной связи — алгоритм медленного старта, разработанный Джейкобсоном. Этот алгоритм проводит многочисленные проверки, пытаясь узнать пропускную способность сети. В высокоскоростных сетях на проведение пяти-шести тестов для определения отклика сети тратится огромное количество сетевых ресурсов. Более эффективная схема состоит в резервировании ресурсов отправителем, получателем и сетью в момент установления соединения. Кроме того, заблаговременное резервирование ресурсов позволяет несколько снизить джиттер. Иными словами, рост скоростей передачи неумолимо подталкивает разработчиков к использованию сетей, ориентированных на установление соединения.
Еще одно полезное свойство для протокола — возможность отправлять нормальное количество данных вместе с запросом соединения. Благодаря этому можно сэкономить один RTT.
6.8. Резюме
Транспортный уровень — это ключ к пониманию многоуровневых протоколов. Он предоставляет различные службы, важнейшей из которых является сквозной, надежный, требующий соединения поток байтов от отправителя к получателю. Доступ к нему осуществляется с помощью служебных примитивов: с их помощью можно устанавливать, использовать и разрывать соединения. Общепринятый интерфейс транспортного уровня обеспечивается сокетами Беркли.
Транспортные протоколы должны управлять соединением в ненадежных сетях. Установление соединения осложняется возможностью наличия дубликатов пакетов, которые могут появляться в самый неподходящий момент. Для борьбы с ними при установлении соединения применяется алгоритм «тройного рукопожатия». Разрыв соединения — более простой, но далеко не тривиальный процесс из-за проблемы «двух армий».
Даже если сетевой уровень абсолютно надежен, у транспортного уровня немало работы. Он должен обрабатывать все служебные примитивы, управлять соединениями и таймерами, распределять пропускную способность и осуществлять контроль перегрузки, а также использовать раздвижное окно переменного размера для управления потоком данных.
Контроль перегрузки должен справедливо распределять пропускную способность между конкурирующими потоками и отслеживать изменения в использовании сети. Закон управления AIMD позволяет получить эффективное и справедливое распределение.
Главными транспортными интернет-протоколами являются TCP и UDP. UDP — протокол без установления соединения. Он работает с IP-пакетами и обеспечивает мультиплексирование и демультиплексирование нескольких процессов с использованием единого IP-адреса. UDP может применяться при клиент-серверных взаимодействиях, например при удаленном вызове процедур (RPC). Кроме того, на его основе можно создавать протоколы реального времени (например, RTP).
TCP — наиболее распространенный интернет-протокол. Он обеспечивает надежный дуплексный поток байтов с контролем перегрузки. Для всех сегментов применяется 20-байтный заголовок. Оптимизации производительности TCP было уделено много внимания. Для этого в нем применяются алгоритмы Нейгла (Nagle), Кларка (Clark), Джейкобсона (Jacobson), Карна (Karn) и др.
Хотя UDP и TCP прекрасно справляются со своей задачей на протяжении многих лет, они оставляют большой простор для внесения улучшений с целью повышения производительности и решения проблем, порождаемых современными высокоскоростными сетями. Такими современными доработками, в частности, являются CUBIC TCP, QUIC и BBR.
Производительность сети обычно зависит от протокола и накладных расходов по обработке сегментов, а с увеличением скорости передачи данных ситуация ухудшается. Протоколы должны разрабатываться так, чтобы минимизировать число сегментов и оставаться работоспособными при больших значениях задержки передачи. Для гигабитных сетей лучше всего подходят простые протоколы и потоковая обработка.
Вопросы и задачи
1. В нашем примере транспортных примитивов, приведенных на илл. 6.2, LISTEN является блокирующим вызовом. Обязательно ли это? Если нет, объясните, как следует пользоваться неблокирующим примитивом. Какое преимущество это даст по сравнению с представленной нами схемой?
2. Приложение для общения в чате, использующее протокол TCP, многократно вызывает функцию получения данных receive() и выводит полученные данные как новое сообщение. К какой проблеме может привести такой подход?
3. В модели, лежащей в основе диаграммы состояний на илл. 6.4, предполагается, что пакеты могут теряться на сетевом уровне и поэтому должны подтверждаться индивидуально. Допустим, сетевой уровень обеспечивает 100 %-ную надежность доставки и никогда не теряет пакеты. Нужны ли какие-нибудь изменения в диаграмме состояний на илл. 6.4, и если да, то какие?
4. В обеих частях илл. 6.6 значение SERVER_PORT должно быть одинаковым у клиента и у сервера. Почему это так важно?
5. Предположим, что используется управляемая таймером схема генерирования начальных порядковых номеров с 15-разрядным счетчиком тактовых импульсов. Таймер срабатывает один раз в 100 мс, а максимальное время жизни пакета равно 60 с. Как часто должна производиться ресинхронизация:
а) в наихудшем случае?
б) если на данные тратится 240 порядковых номеров в минуту?
6. Почему максимальное время жизни пакета T должно быть достаточно большим, чтобы гарантировать, что не только пакет, но и его подтверждение исчезли?
7. Представьте протокол транспортного уровня, ориентированный на установление соединения, который выбирает порядковые номера для пакетов на основе времени суток. Таймер использует 10-битный счетчик и срабатывает с интервалом в 125 мс. Максимальное время жизни пакета — 64 с. Если отправитель передает по 4 пакета/с, как долго соединение будет работать без захода в запретную зону?
8. Объясните, в чем состоит разница между использованием протокола раздвижного окна на канальном уровне и его использованием на транспортном уровне с точки зрения тайм-аутов протокола.
9. Рассмотрим проблему восстановления после сбоев хостов (илл. 6.18). Если бы интервал между записью и отправкой подтверждения (или наоборот) можно было сделать относительно небольшим, какими были бы две лучшие стратегии отправителя и получателя, минимизирующие риск ошибки протокола?
10. В сеть на илл. 6.20 добавляется новый поток E, который идет через маршрутизаторы R1, R2 и R6. Как изменится распределение пропускной способности по максиминному критерию для пяти потоков?
11. Допустим, потоки на илл. 6.20 реорганизованы так, что поток A проходит через маршрутизаторы R1, R2, R3, B — через R1, R2, R5, R6, C — через R4, R2, R3, а D — через R4, R2, R3. Как при этом будет выглядеть распределение пропускной способности по максиминному критерию?
12. Обсудите преимущества и недостатки схемы кредитного протокола по сравнению с протоколами раздвижного окна.
13. Существуют и другие стратегии, обеспечивающие равнодоступность при контроле перегрузки: аддитивное увеличение, аддитивное уменьшение (Additive Increase Additive Decrease, AIAD); мультипликативное увеличение, аддитивное уменьшение (Multiplicative Increase Additive Decrease, MIAD); мультипликативное увеличение, мультипликативное уменьшение (Multiplicative Increase Multiplicative Decrease, MIMD). Что вы можете сказать об их сходимост