Контроль перегрузки TCP - быстрое восстановление в графике

Я читал книгу «Компьютерные сети: подход сверху вниз» и столкнулся с вопросом, который, похоже, не понимаю.

На время чтения TCP Congestion Control имеет три состояния: медленный запуск, Предотвращение перегрузки и быстрое восстановление. Я хорошо понимаю медленный запуск и предотвращение перегрузки, но быстрое восстановление довольно расплывчато. В книге утверждается, что TCP ведет себя следующим образом: (cwnd = Окно перегрузки) введите здесь описание изображения
Давайте посмотрим на следующий график:  График размера окна раунда передачи и перегрузки

Как мы видим, в раунде 16 отправитель отправляет 42 сегмента, и поскольку размер окна перегрузки был уменьшен вдвое (+3), мы можем сделать вывод, что было 3 дублирующих ACK. В ответе на этот вопрос утверждается, что раунды с 16 по 22 находятся в состоянии предотвращения перегрузки. Но почему бы не Быстрое восстановление? Я имею в виду, что после трех дублированных ACK TCP входит в быстрое восстановление, а каждый второй дублированный ACK должен увеличивать окно перегрузки. Почему на графике это не представлено? Единственное разумное объяснение, которое я мог придумать, заключается в том, что на этом графике было только три дублирующих ACK, и ACK, которые были получены с тех пор, не были дубликатами.

Даже если это так, как график выглядел бы так, если бы было более 3-х дублированных ACK? **

Есть ли какое-нибудь представление о быстром восстановлении на графике выше? Почему нет / да?

** Я долго не мог ответить на этот вопрос. Я буду очень рад любому ответу, спасибо!

ОБНОВЛЕНИЕ вот изображение. Я думаю, что раунд определяется как когда все сегменты в окне принимаются. На фото круг показан кружком. введите описание изображения здесьПочему cwnd экспоненциально растет в состоянии быстрого восстановления? (на картинке я случайно написал целесообразно, а не экспоненциально)


person Noam Solovechick    schedule 13.06.2015    source источник


Ответы (2)


ОБНОВЛЕНИЕ: мой первоначальный ответ согласился с решением, но после тщательного обдумывания я считаю, что решение неверное. Этот ответ был переписан заново; пожалуйста, прочтите внимательно. Я показываю, почему быстрое восстановление вводится в момент времени T = 16 и почему протокол остается там до T = 22. Данные на графике подтверждают мою теорию, поэтому я почти уверен, что решение совершенно неверное.

Давайте начнем с того, что кое-что проясним: медленный старт растет экспоненциально; Предотвращение перегрузки растет линейно, а быстрое восстановление растет линейно, даже если для обновления значения cwnd используется та же формула, что и при медленном запуске.

Позвольте мне уточнить.

Почему мы говорим, что "Медленный старт" растет cwnd экспоненциально?

Обратите внимание, что cwnd увеличивается на MSS байта для каждого полученного ACK.

Посмотрим на пример. Предположим, что cwnd инициализируется значением 1 MSS (значение MSS обычно составляет 1460 байт, поэтому на практике это означает, что cwnd инициализируется значением 1460). На этом этапе, поскольку размер окна перегрузки может содержать только 1 пакет, TCP не будет отправлять новые данные, пока этот пакет не будет подтвержден. Предполагая, что ACK не теряются, это означает, что примерно один новый пакет передается каждые RTT секунды (напомним, что RTT - это время приема-передачи), поскольку нам нужно (1/2) * RTT для отправки пакета и ( 1/2) * RTT для прихода ACK.

Таким образом, скорость передачи составляет примерно MSS / RTT бит / с. Теперь помните, что для каждого ACK cwnd увеличивается на MSS. Таким образом, как только приходит первый ACK, cwnd становится 2*MSS, так что теперь мы можем отправить 2 пакета. Когда эти два пакета подтверждены, мы увеличиваем cwnd дважды, так что теперь cwnd равно 4*MSS. Большой! Мы можем отправить 4 пакета. Эти 4 пакета подтверждены, поэтому мы увеличиваем cwnd в 4 раза! Итак, у нас есть cwnd = 8*MSS. И тогда мы получаем cwnd = 16*MSS. По сути, мы удваиваем cwnd каждые RTT-секунды (это также объясняет, почему cwnd = cwnd+MSS*(MSS/cwnd) в предотвращении перегрузки приводит к линейному росту)

Да, это сложно, формула cwnd = cwnd+MSS легко приводит нас к мысли, что она линейна - распространенное заблуждение, потому что люди часто забывают, что это применяется к каждому подтвержденному пакету.

Обратите внимание, что в реальном мире передача 4 пакетов не обязательно генерирует 4 ACK. Он может генерировать только 1 ACK, но поскольку TCP использует совокупные ACK, этот единственный ACK все еще подтверждает 4 пакета.

Почему быстрое восстановление является линейным?

Формула cwnd = cwnd+MSS применяется как для медленного запуска, так и для предотвращения перегрузки. Можно было бы подумать, что это заставляет оба состояния вызывать экспоненциальный рост. Однако быстрое восстановление применяет эту формулу в другом контексте: когда получен дублирующийся ACK. В этом заключается разница: при медленном запуске один RTT подтвердил целую группу сегментов, и каждый подтвержденный сегмент внес + 1MSS в новое значение cwnd, тогда как при быстром восстановлении дублированный ACK тратит RTT, чтобы подтвердить потерю один сегмент, поэтому вместо обновления cwnd N раз за каждые RTT-секунды (где N - количество переданных сегментов) мы обновляем cwnd один раз для сегмента, который был ПОТЕРЯН. Таким образом, мы «потратили» один круговой обход всего на один сегмент, поэтому мы увеличиваем cwnd только на 1.

О предотвращении перегрузки - об этом я объясню ниже, анализируя график.

Анализ графика

Хорошо, давайте посмотрим, что именно происходит на этом графике, шаг за шагом. Ваша картина в какой-то степени верна. Позвольте мне сначала прояснить некоторые вещи:

  1. Когда мы говорим, что медленный старт и быстрое восстановление растут экспоненциально, это означает, что он растет экспоненциально раунд за раундом, как показано на картинке. Итак, это правильно. Вы правильно определили раунды с синими кружками: обратите внимание, как значения cwnd экспоненциально растут от одного кружка к другому - 1, 2, 4, 8, 16, ...
  2. На вашем изображении, кажется, написано, что после медленного старта протокол переходит в быстрое восстановление. Это не то, что происходит. Если бы он перешел на быстрое восстановление из медленного старта, мы бы увидели, что cwnd уменьшится вдвое. Это не то, что показывает график: значение cwnd не уменьшается вдвое с T = 6 до T = 7.

Хорошо, теперь давайте посмотрим, что именно происходит в каждом раунде. Обратите внимание, что единица времени на графике - раунд. Итак, если в момент времени T = X мы передаем N сегментов, то предполагается, что в момент времени T = X + 1 эти N сегментов были подтверждены (конечно, при условии, что они не были потеряны).

Также обратите внимание, как мы можем определить значение ssthresh, просто взглянув на график. При T = 6 cwnd перестает экспоненциально расти и начинает расти линейно, и его значение не уменьшается. Единственно возможный переход от медленного старта к другому состоянию, не связанному с уменьшением cwnd, - это переход к предотвращению перегрузки, который происходит, когда размер окна перегрузки равен ssthresh. На графике видно, что это происходит, когда cwnd равно 32. Итак, мы сразу знаем, что ssthresh инициализирован равным 32 MSS. В книге показан очень похожий график на странице 276 (рис. 3.53), где авторы делают аналогичный вывод:

введите описание изображения здесь

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

Наконец, предположим, что MSS составляет не менее 1460 байтов (обычно это 1460 байтов, потому что Ethernet имеет MTU = 1500 байтов, и нам нужно учитывать размер заголовков TCP + IP, которые вместе требуют 40 байтов). Это важно видеть, когда cwnd превышает ssthresh, поскольку единица измерения cwnd равна MSS, а ssthresh выражается в байтах.

Итак, начнем:

T = 1:

cwnd = 1 MSS; ssthresh = 32 КБ

Передать 1 сегмент

T = 2

1 сегмент подтвержден

cwnd + = 1; ssthresh = 32 КБ

Новое значение cwnd: 2

Передать 2 сегмента

T = 3

2 сегмента подтверждены

cwnd + = 2; ssthresh = 32 КБ

Новое значение cwnd: 4

Передать 4 сегмента

T = 4

4 сегмента подтверждены

cwnd + = 4; ssthresh = 32 КБ

Новое значение cwnd: 8

Передача 8 сегментов

T = 5

8 сегментов подтверждены

cwnd + = 8; ssthresh = 32 КБ

Новое значение cwnd: 16

Передача 16 сегментов

T = 6

16 сегментов подтверждены

cwnd + = 16; ssthresh = 32 КБ

Новое значение cwnd: 32

Передача 32 сегмента

Хорошо, посмотрим, что теперь будет. cwnd достигло ssthresh (32 * 1460 = 46720 байт, что больше 32000). Пора переходить к предотвращению заторов. Обратите внимание, как значения cwnd экспоненциально растут через раунды, потому что каждый подтвержденный пакет вносит 1 MSS в новое значение cwnd, и каждый отправленный пакет подтверждается в следующем раунде.

Переход к предотвращению заторов

Теперь cwnd не будет увеличиваться экспоненциально, потому что каждый ACK больше не будет вносить 1 MSS. Вместо этого каждый ACK вносит свой вклад с MSS*(MSS/cwnd). Так, например, если MSS составляет 1460 байтов, а cwnd - 14600 байтов (поэтому в начале каждого раунда мы отправляем 10 сегментов), то каждый ACK (при условии, что один ACK на сегмент) увеличит cwnd на 1/10 MSS (146 байтов). . Поскольку мы отправляем 10 сегментов и в конце раунда мы предполагаем, что каждый сегмент был подтвержден, то в конце раунда мы увеличили cwnd на 10 * 1/10 = 1. Другими словами, каждый сегмент вносит небольшую долю в cwnd, так что мы просто увеличиваем cwnd на 1 MSS каждый раунд. Таким образом, теперь каждый раунд увеличивает cwnd на 1, а не на количество сегментов, которые были переданы / подтверждены.

Мы будем избегать перегрузки до тех пор, пока не будет обнаружена какая-либо потеря (либо 3 повторяющихся ACK, либо тайм-аут).

А теперь позвольте часам возобновить работу ...

T = 7

32 сегмента подтверждены

cwnd + = 1; ssthresh = 32 КБ

Новое значение cwnd: 33

Передать 33 сегмента

Обратите внимание, как cwnd перешел с 32 на 33, хотя было подтверждено 32 сегмента (каждый ACK, таким образом, составляет 1/32). Если бы мы были в медленном старте, как в T = 6, у нас было бы cwnd += 32. Это новое значение cwnd также согласуется с тем, что мы видим на графике в момент времени T = 7.

T = 8

33 сегмента признаны

cwnd + = 1; ssthresh = 32 КБ

Новое значение cwnd: 34

Передать 34 сегмента

T = 9

34 сегмента признаны

cwnd + = 1; ssthresh = 32 КБ

Новое значение cwnd: 35

Передача 35 сегментов

Обратите внимание, что это согласуется с графиком: при T = 9 у нас есть cwnd = 35. Это продолжается до Т = 16 ...

T = 10

35 сегментов признаны

cwnd + = 1; ssthresh = 32 КБ

Новое значение cwnd: 36

Передать 36 сегментов

T = 11

36 сегментов признаны

cwnd + = 1; ssthresh = 32 КБ

Новое значение cwnd: 37

Передать 37 сегментов

T = 12

37 сегментов признаны

cwnd + = 1; ssthresh = 32 КБ

Новое значение cwnd: 38

Передать 38 сегментов

T = 13

38 сегментов признаны

cwnd + = 1; ssthresh = 32 КБ

Новое значение cwnd: 39

Передать 39 сегментов

T = 14

39 сегментов признаны

cwnd + = 1; ssthresh = 32 КБ

Новое значение cwnd: 40

Передача 40 сегментов

T = 15

40 сегментов подтверждены

cwnd + = 1; ssthresh = 32 КБ

Новое значение cwnd: 41

Передать 41 сегмент

T = 16

41 сегмент признан

cwnd + = 1; ssthresh = 32 КБ

Новое значение cwnd: 42

Передать 42 сегмента

ПАУЗА

Что происходит сейчас? График показывает, что размер окна перегрузки уменьшается примерно до половины своего размера, а затем снова увеличивается линейно по циклам. Единственная возможность состоит в том, что было 3 дублирующихся ACK и протокол переключается на быстрое восстановление. График показывает, что он НЕ переключается на медленный запуск, потому что это снизит cwnd до 1. Таким образом, единственный возможный переход - это быстрое восстановление.

Введя быстрое восстановление, получаем ssthresh = cwnd/2. Помните, что единицы измерения cwnd - это MSS, а ssthresh - в байтах, мы должны быть осторожны с этим. Таким образом, новое значение ssthresh = cwnd*MSS/2 = 42*1460/2 = 30660.

Опять же, это соответствует графику; обратите внимание, что ssthresh будет достигнуто в ближайшем будущем, когда cwnd будет немного меньше 30 (напомним, что при MSS = 1460 соотношение не совсем 1: 1, поэтому мы достигли порогового значения, даже если размер окна перегрузки немного ниже 30 ).

Переключение на предотвращение перегрузки также приводит к тому, что новое значение cwnd становится ssthresh+3MSS = 21+3 = 24 (не забудьте быть осторожным с единицами измерения, здесь я снова преобразовал ssthresh в MSS, потому что наши значения cwnd учитываются в MSS).

На данный момент мы избегаем перегрузок с T = 17, ssthresh = 30660 bytes и cwnd = 24.

При вводе T = 18 могут произойти две вещи: либо мы получаем дублирующийся ACK, либо нет. Если мы этого не сделаем (это новый ACK), мы перейдем к предотвращению перегрузки. Но это снизит cwnd до значения ssthresh, равного 21. Это не соответствует графику - график показывает, что cwnd продолжает линейно расти. Кроме того, он не переключается на медленный запуск, потому что это снизит cwnd до 1. Это означает, что быстрого восстановления не осталось, и мы получаем дублирующиеся ACK. Это происходит до момента времени T = 22:

T = 18

Получен дубликат ACK

cwnd + = 1; ssthresh = 30660 байт

Новое значение cwnd: 25

T = 19

Получен дубликат ACK

cwnd + = 1; ssthresh = 30660 байт

Новое значение cwnd: 26

T = 20

Получен дубликат ACK

cwnd + = 1; ssthresh = 30660 байт

Новое значение cwnd: 27

T = 21

Получен дубликат ACK

cwnd + = 1; ssthresh = 30660 байт

Новое значение cwnd: 28

T = 22

Получен дубликат ACK

cwnd + = 1; ssthresh = 30660 байт

Новое значение cwnd: 29

** ПАУЗА **

Мы все еще находимся в режиме быстрого восстановления, и теперь внезапно cwnd падает до 1. Это показывает, что он снова входит в режим медленного запуска. Новое значение ssthresh будет 29*1460/2 = 21170 и cwnd = 1. Это также означает, что, несмотря на наши усилия по ретрансляции сегмента, был тайм-аут.

T = 23

cwnd = 1; ssthresh = 21170 байт

Передать 1 сегмент

T = 24

1 сегмент подтвержден

cwnd + = 1; ssthresh = 21170 байт

Новое значение cwnd: 2

Передать 2 сегмента

T = 25

2 сегмента подтверждены

cwnd + = 2; ssthresh = 21170 байт

Новое значение cwnd: 4

Передать 4 сегмента

T = 26

4 сегмента подтверждены

cwnd + = 4; ssthresh = 21170 байт

Новое значение cwnd: 8

Передача 8 сегментов

...

Надеюсь, это проясняет.

person Filipe Gonçalves    schedule 13.06.2015
comment
Спасибо. Вы объяснили это очень четко, но можете ли вы объяснить, почему быстрое восстановление не является линейным? Я имею в виду, что для каждого dupACK окно увеличивается на 1. Если бы я нарисовал это на графике, это было бы y = x (где x - дополнительное количество повторяющихся ACK сверх 3, а y - увеличение cwnd). - person Noam Solovechick; 13.06.2015
comment
@NoamSolovechick Рад, что смог помочь. См. Обновленный ответ. - person Filipe Gonçalves; 13.06.2015
comment
Посмотрите на обновление, я опубликовал фотографию того, почему я ошибочно считаю, что быстрое восстановление является линейным, и я также сравнил его с медленным запуском, который не является линейным. Большое вам спасибо за ваше время. - person Noam Solovechick; 13.06.2015
comment
@NoamSolovechick Упс, извините за мое молчание, по какой-то причине я пропустил уведомление SO для комментария к этому ответу. Вскоре я рассмотрю ваш обновленный вопрос и постараюсь уточнить свой ответ. - person Filipe Gonçalves; 16.06.2015
comment
@NoamSolovechick Обновил мой ответ. В основном переписан с нуля, так как после тщательного анализа я считаю, что решение неверное. - person Filipe Gonçalves; 17.06.2015
comment
Вау, ты потрясающий! Большое спасибо! Я уверен, что это поможет многим другим, кто также изучит основные принципы управления перегрузкой TCP. Спасибо!!!! :) - person Noam Solovechick; 17.06.2015
comment
@NoamSolovechick Рад, что смог помочь! Спасибо и за отличный вопрос ... такие хорошие вопросы - вот что делает SO потрясающим онлайн-ресурсом :) - person Filipe Gonçalves; 17.06.2015
comment
Почему начальное значение ssthresh = 32kb? So here we go: T = 1: cwnd = 1 MSS; ssthresh = 32 kB - person Mahsolid; 01.03.2017
comment
До Т = 18, как вы получили значение ssthresh равным 21? cwnd to be ssthresh+3MSS = 21+3 = 24 (remember to be careful with units, here I converted ssthresh into MSS again because our values of cwnd are counted in MSS). - person Mahsolid; 01.03.2017
comment
Быстрое восстановление не является линейным, потому что вы получаете более одного дублирующего ACK на один RTT. - person arnoapp; 18.03.2017

В TCP Reno (версия TCP, включающая Fast Recovery) график cwnd (окно перегрузки) должен выглядеть следующим образом:

введите здесь описание изображения

Только одно время RTT между Slow Start и Congestion Avoidance равно Fast Recovery. Если, как на графике в книге "Компьютерные сети: подход сверху вниз", просто используйте прямую линию в T16 для представления Fast Recovery процесса, тогда cwnd в T17 должно быть 21 MSS вместо (21 + 3) MSS, потому что при переходе с Fast Recovery на Congestion Avoidance cwnd будет уменьшаться до значения ssthresh. Итак, график в книге неправильный. Кроме того, ответ @Filipe Gonçalves тоже неверен.

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

ссылка:

1. http://www.ijcse.com/docs/INDJCSE17-08-03-113.pdf 2. https://www.isi.edu/nsnam/DIRECTED_RESEARCH/DR_WANIDA/DR/JavisInActionFastRecoveryFrame.html

person toggler    schedule 29.03.2020