Задержки (а по‑простому тормоза) в работе сети вряд ли кого‑то оставят равнодушным. Мы все очень не любим, когда медленно загружаются наши любимые веб‑ресурсы, или когда обрывается на 99% загрузка какого‑либо файла. Причин тормозов в сети может быть много — от проблем на физическом уровне и до медленной работы на уровне приложений. Сегодня мы поговорим об одной из причин задержек в сети, которую можно выявить с помощью анализатора пакета Wireshark. При этом не имеет особого значения, передается ли наш трафик в открытом виде или прячется за SSL: на верхние уровни мы забираться не будем. Важно только то, что это TCP трафик.
Окна в TCP
Протокол TCP содержит в своей архитектуре ряд функций, позволяющих препятствовать потере пакетов — например, такие механизмы, как повторные передачи и дублирующие подтверждения. Для предотвращения начала потери пакетов в TCP используется механизм скользящего окна (sliding‑window).
В механизме скользящего окна используется окно приема на стороне получателя пакетов для управления потоком данных. Принимающий хост указывает размер окна приема в байтах, сохраняет его в ТСР‑заголовке и сообщает передающему хосту, сколько данных он готов сохранить в памяти своего ТСР‑буфера. Именно в этой памяти данные временно хранятся до тех пор, пока они не будут переданы вверх по стеку протоколов на уровень приложений для последующей обработки. В итоге передающий хост может одновременно послать только то количество данных, которое указано в поле Window size (Размер окна) заголовка, полученного в ответ пакета ТСР. Чтобы передающий хост мог послать больше данных, принимающий хост должен отправить ему подтверждение о получении предыдущей порции данных. Он должен также очистить память своего ТСР‑буфера, обработав данные, занимающие эту память.
В целом, все достаточно просто. Принимающая сторона говорит, что готова принять, условно, три блока данных, отправитель передает их. Получает отправляет подтверждение, после чего отправляются следующие три блока данных и так далее.
В идеальном мире с постоянной пропускной способностью каналов размер окна действительно был бы постоянным значением. Однако, в реальности загрузка канала у нас постоянно меняется, и вместе с ней меняется и размер Window size. Процесс изменения размеров окна приема действует как в сторону уменьшения, так и увеличения. Так, если сервер способен обработать данные с большей скоростью, он может послать подтверждающий пакет АСК с большим размером окна приема.
Но ниже представлен пример приложения, у которого по каким‑то причинам уменьшился объем памяти, доступной в буфере, в результате чего получатель был вынужден уменьшить размер окна.
В случае, если получателю удалось решить свои проблемы с памятью, окно может быть снова увеличено. Но если быстро решить проблемы не удалось, и есть риск, что следующий блок данных обработать не удастся, получатель может указать размер Window=0. Что произойдет в таком случае?
Получив такой пакет, клиент прервет передачу данных и будет периодически отправлять пакет поддержания активным соединения. По сути, мы просто поддерживаем TCP соединение, при этом не передавая никаких данных. Когда получатель наконец‑то решит свои проблемы с памятью, он отправит пакет с обновлением размеров окна и передача полезной нагрузки продолжится.
От теории к практике
Теперь давайте вооружимся Wireshark и посмотрим, как выглядит соответствующий трафик.
В примере ниже у нас происходит информационный обмен между 192.168.0.20 и 192.168.0.30. В первых трех ACK пакетах значение Window уменьшается, видимо, у получателя проблемы с памятью и он не может обрабатывать больший объем данных.
Затем получатель отправляет пакет с нулевым окном. Однако после этого, по прошествии небольшого интервала времени, он отправляет обновление информации о размерах окна, в результате чего передача полезной нагрузки возобновляется.
В самом пакете информация о размере окна находится в TCP заголовке:
Теперь посмотрим более печальный случай: когда получателю не удается быстро решить свои проблемы с памятью и значение Window остается нулевым некоторое время.
Здесь мы видим обмен трафиком между двумя узлами. В какой‑то момент один из участников отказывается принимать полезную нагрузку, отправив TCP ZeroWindow. После этого второй участник обмена с некоторой периодичностью отправляет Keep‑Alive пакеты, на которые первый участник продолжает отвечать нулевыми окнами. Пакеты Keep‑Alive также являются служебными и не несут в себе никакой полезной нагрузки.
Как искать
В приведенных примерах наши служебные пакеты были представлены в уже отфильтрованном виде без лишнего трафика. В реальных дампах у нас будет много других пакетов, и нам потребуется осуществить фильтрацию.
Для того чтобы отфильтровать в Wireshark пакеты с определенным размером окна, нужно воспользоваться фильтром tcp.window_size_value
.
Например, для того, чтобы найти все пакеты с ненулевым размером окна, нужно указать tcp.window_size_value > 0
Для фильтрации пакетов с ZeroWindow можно, конечно, явно указать фильтр tcp.window_size_value == 0
, а можно воспользоваться фильтром
tcp.analysis.zero_window
Для обнаружения Keep_Alive воспользуемся фильтром tcp.analysis.keep_alive
Также узнать, как с течением времени менялось значение TCP Window, можно с помощью графика. Для этого нужно выбрать Statistics → TCP Stream Graph → Window Scaling.
Заключение
Механизм скользящего окна, о котором мы сегодня говорили, непосредственно связан с неспособностью сервера принимать и обрабатывать данные. Любое уменьшение размера окна приема или даже его установка в нулевое состояние является непосредственным результатом какого‑то затруднения, возникшего на сервере. Поэтому, если вы видите большое количество таких пакетов в трафике, то это верный признак проблем с производительностью на принимающем узле и возможная причина задержек и сбоев в работе сетевых приложений.
В завершение рекомендую открытые уроки в Otus, подробнее о них ниже. Занятия пройдут в онлайн-формате и будут полезны сетевым инженерам, сетевым архитекторам и всем, кто глубоко интересуется современными сетевыми технологиями.
5 февраля: ISIS vs. OSPF, или почему же ISIS до сих пор проигрывает битву OSPF в корпоративных сетях? Записаться
18 февраля: Надежность и сети, или почему скорость сходимости STP уже очень давно никому не нравится? Записаться
lex899
Всю жизнь думал что это работает не так.
Отправляем последовательно (или в любом порядке чтобы навести хаоса) блоки 1, 2, 3.
Если к моменту отправки 4 блока от получателя пришёл ACK на некоторое количество блоков - сдвигаем окно на это количество блоков, продолжаем посылать дальше.
Передача блоков и подтверждений идёт синхронно в обе стороны а не последовательно как на вашей картинке, при этом в случае с размером окна 3 получатель может прислать от 0 до 3х подтверждений в зависимости от того какие блоки и в каком порядке успеют дойти.
А если проблемы не с получателем а с сетью и половина пакетов не доходит - размер окна случайно не будет падать?...