Окно приема TCP

Я пытаюсь понять, как окно приемника влияет на пропускную способность при соединении с высокой задержкой.

У меня есть простая пара клиент-серверных приложений на двух машинах, расположенных далеко друг от друга, с соединением между двумя RTT с задержкой 250 мс. Я провел этот тест как с Windows (XP, 7), так и с Linux (Ubuntu 10.x) с одинаковыми результатами, поэтому для простоты давайте предположим случай: Клиент получает данные: WinXP Pro Сервер отправляет данные: Win7 Pro Опять же, задержка составляет 250 мс RTT.

Я запускаю свой TCP-тест, не меняя размер буфера приемника на клиенте (по умолчанию 8 КБ), и вижу в сети (используя Wireshark):

  • клиент отправляет ACKS на сервер, а пакеты TCP содержат RWIN=65k
  • сервер отправляет данные и сообщает RWIN=65k

Глядя на трассировку, я вижу всплески из 3-4 пакетов (с полезной нагрузкой 1460 байт), сразу за которыми следует ACK, отправленный с клиентской машины на сервер, затем ничего в течение примерно 250 мс, затем новый пакет пакетов с сервера клиенту.

Итак, в заключение получается, что сервер не отправляет новые данные еще до того, как заполнит окно получателя.

Чтобы провести дополнительные тесты, я также провел тот же тест, на этот раз изменив размер буфера получателя на клиентской машине (в Windows изменение размера буфера получателя в конечном итоге влияет на RWIN, объявленный машиной). Я ожидаю увидеть большой пакет пакетов перед блокировкой для ACK... и, по крайней мере, более высокую пропускную способность.

В этом случае я установил размер буфера recv на 100 000 000. Пакеты от клиента к серверу теперь имеют RWIN=99 999 744 (ну, это хорошо), но, к сожалению, шаблон данных, отправляемых с сервера клиенту, остается прежним: короткий пакет с последующим долгим ожиданием. Чтобы подтвердить то, что я вижу в сети, я также измеряю время, необходимое для отправки порции данных с сервера клиенту. Я не вижу НИКАКИХ изменений в использовании большого RWIN или использовании по умолчанию.

Может ли кто-нибудь помочь мне понять, почему изменение RWIN на самом деле не влияет на пропускную способность?

Несколько замечаний: - сервер отправляет данные как можно быстрее, используя функцию записи () фрагментов по 8 КБ - как я уже говорил ранее, я наблюдаю аналогичные эффекты и при использовании Linux. изменение размера буфера приемника влияет на RWIN, используемый узлом, но пропускная способность остается прежней. - Я анализирую трассировку после нескольких сотен пакетов, чтобы дать механизму медленного старта TCP достаточно времени для увеличения размера CWIN.


Как и было предложено, я добавляю сюда небольшой снимок трассировки провода.

No.     Time        Source                Destination           Protocol Length Info
     21 2.005080    CCC.CCC.CCC.CCC       sss.sss.sss.sss       TCP      60     57353 > 21500 [ACK] Seq=1 Ack=11681 Win=99999744 Len=0
     22 2.005109    sss.sss.sss.sss       CCC.CCC.CCC.CCC       TCP      1514   21500 > 57353 [ACK] Seq=19305 Ack=1 Win=65536 Len=1460
     23 2.005116    sss.sss.sss.sss       CCC.CCC.CCC.CCC       TCP      1514   21500 > 57353 [ACK] Seq=20765 Ack=1 Win=65536 Len=1460
     24 2.005121    sss.sss.sss.sss       CCC.CCC.CCC.CCC       TCP      1514   21500 > 57353 [ACK] Seq=22225 Ack=1 Win=65536 Len=1460
     25 2.005128    sss.sss.sss.sss       CCC.CCC.CCC.CCC       TCP      946    21500 > 57353 [PSH, ACK] Seq=23685 Ack=1 Win=65536 Len=892
     26 2.005154    CCC.CCC.CCC.CCC       sss.sss.sss.sss       TCP      60     57353 > 21500 [ACK] Seq=1 Ack=14601 Win=99999744 Len=0
     27 2.007106    CCC.CCC.CCC.CCC       sss.sss.sss.sss       TCP      60     57353 > 21500 [ACK] Seq=1 Ack=16385 Win=99999744 Len=0
     28 2.007398    sss.sss.sss.sss       CCC.CCC.CCC.CCC       TCP      1514   21500 > 57353 [ACK] Seq=24577 Ack=1 Win=65536 Len=1460
     29 2.007401    sss.sss.sss.sss       CCC.CCC.CCC.CCC       TCP      1514   21500 > 57353 [ACK] Seq=26037 Ack=1 Win=65536 Len=1460
     30 2.007403    sss.sss.sss.sss       CCC.CCC.CCC.CCC       TCP      1514   21500 > 57353 [ACK] Seq=27497 Ack=1 Win=65536 Len=1460
     31 2.007404    sss.sss.sss.sss       CCC.CCC.CCC.CCC       TCP      1514   21500 > 57353 [ACK] Seq=28957 Ack=1 Win=65536 Len=1460
     32 2.007406    sss.sss.sss.sss       CCC.CCC.CCC.CCC       TCP      1514   21500 > 57353 [ACK] Seq=30417 Ack=1 Win=65536 Len=1460
     33 2.007408    sss.sss.sss.sss       CCC.CCC.CCC.CCC       TCP      946    21500 > 57353 [PSH, ACK] Seq=31877 Ack=1 Win=65536 Len=892
     34 2.007883    CCC.CCC.CCC.CCC       sss.sss.sss.sss       TCP      60     57353 > 21500 [ACK] Seq=1 Ack=19305 Win=99999744 Len=0
     35 2.257143    CCC.CCC.CCC.CCC       sss.sss.sss.sss       TCP      60     57353 > 21500 [ACK] Seq=1 Ack=22225 Win=99999744 Len=0
     36 2.257160    CCC.CCC.CCC.CCC       sss.sss.sss.sss       TCP      60     57353 > 21500 [ACK] Seq=1 Ack=24577 Win=99999744 Len=0
     37 2.257358    sss.sss.sss.sss       CCC.CCC.CCC.CCC       TCP      1514   21500 > 57353 [ACK] Seq=32769 Ack=1 Win=65536 Len=1460
     38 2.257362    sss.sss.sss.sss       CCC.CCC.CCC.CCC       TCP      1514   21500 > 57353 [ACK] Seq=34229 Ack=1 Win=65536 Len=1460
     39 2.257364    sss.sss.sss.sss       CCC.CCC.CCC.CCC       TCP      1514   21500 > 57353 [ACK] Seq=35689 Ack=1 Win=65536 Len=1460
     40 2.257365    sss.sss.sss.sss       CCC.CCC.CCC.CCC       TCP      1514   21500 > 57353 [ACK] Seq=37149 Ack=1 Win=65536 Len=1460

Как видите, сервер перестал отправлять данные на пакете №33.

Клиент отправляет ACK в пакете № 34 старого пакета (seq = 19305, отправлено в пакете № 20, здесь не показано). С RWIN 100 Мб я ожидаю, что сервер НЕ будет блокироваться какое-то время.

После 20-30 пакетов окно перегрузки на стороне сервера должно быть достаточно большим, чтобы отправить больше пакетов, чем я вижу... Я предполагаю, что окно перегрузки в конечном итоге вырастет до RWIN... но тем не менее, даже после сотни пакетов, шаблон тот же: данные данные затем блокируются на 250 мс...


person fabrizi0    schedule 23.04.2012    source источник
comment
Обе машины подключены к беспроводной сети? Можете ли вы распечатать порядковые номера, отправляемые обратно и четвертым с сервера/клиента в пакетах и ​​подтверждениях. На стороне клиента или сервера может быть много шума, вызывающего потерю данных.   -  person JustinDanielson    schedule 24.04.2012


Ответы (4)


Из предоставленного вами образца я могу предположить две вещи:

  1. Сервер имеет буфер отправки около 15k.
  2. Дамп, который вы предоставляете, был сделан на стороне сервера.

Чтобы окно TCP-соединения масштабировалось до определенного размера, буфер отправки на отправителе и буфер приема на получателе должны быть достаточно большими.

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

Короче говоря, вам нужно настроить размер буфера отправки на сервере.

Чтобы прояснить ситуацию, давайте проанализируем ваш пример пакета за пакетом.

Сервер отправляет еще один пакет данных:

 22 2.005109    sss.sss.sss.sss       CCC.CCC.CCC.CCC       TCP      1514   21500 > 57353 [ACK] Seq=19305 Ack=1 Win=65536 Len=1460
 23 2.005116    sss.sss.sss.sss       CCC.CCC.CCC.CCC       TCP      1514   21500 > 57353 [ACK] Seq=20765 Ack=1 Win=65536 Len=1460
 24 2.005121    sss.sss.sss.sss       CCC.CCC.CCC.CCC       TCP      1514   21500 > 57353 [ACK] Seq=22225 Ack=1 Win=65536 Len=1460
 25 2.005128    sss.sss.sss.sss       CCC.CCC.CCC.CCC       TCP      946    21500 > 57353 [PSH, ACK] Seq=23685 Ack=1 Win=65536 Len=892

Обратите внимание на PSH. Это флаг, указывающий на любые переходы между ними, что полный блок данных был отправлен, и, пожалуйста, отправьте его на другой конец. (В данном случае «полный» фрагмент — это ваши 8 КБ)

Пока сервер все еще отправляет, он получает 2 ACKS:

 26 2.005154    CCC.CCC.CCC.CCC       sss.sss.sss.sss       TCP      60     57353 > 21500 [ACK] Seq=1 Ack=14601 Win=99999744 Len=0
 27 2.007106    CCC.CCC.CCC.CCC       sss.sss.sss.sss       TCP      60     57353 > 21500 [ACK] Seq=1 Ack=16385 Win=99999744 Len=0

Обратите особое внимание на номера: Ack=14601 и Ack=16385. Эти числа являются порядковыми номерами пакетов, которые получатель подтверждает.

Ack=14601 означает "Я получил все до последовательности 14601".

Обратите также внимание, что это более старые данные, которых нет в приведенном вами образце.

Итак, сервер обрабатывает эти ACK и продолжает отправлять данные:

 28 2.007398    sss.sss.sss.sss       CCC.CCC.CCC.CCC       TCP      1514   21500 > 57353 [ACK] Seq=24577 Ack=1 Win=65536 Len=1460
 29 2.007401    sss.sss.sss.sss       CCC.CCC.CCC.CCC       TCP      1514   21500 > 57353 [ACK] Seq=26037 Ack=1 Win=65536 Len=1460
 30 2.007403    sss.sss.sss.sss       CCC.CCC.CCC.CCC       TCP      1514   21500 > 57353 [ACK] Seq=27497 Ack=1 Win=65536 Len=1460
 31 2.007404    sss.sss.sss.sss       CCC.CCC.CCC.CCC       TCP      1514   21500 > 57353 [ACK] Seq=28957 Ack=1 Win=65536 Len=1460
 32 2.007406    sss.sss.sss.sss       CCC.CCC.CCC.CCC       TCP      1514   21500 > 57353 [ACK] Seq=30417 Ack=1 Win=65536 Len=1460
 33 2.007408    sss.sss.sss.sss       CCC.CCC.CCC.CCC       TCP      946    21500 > 57353 [PSH, ACK] Seq=31877 Ack=1 Win=65536 Len=892

Здесь у нас есть полный блок данных: 1460*5+892 == 8192.

Затем, через 0,443 мс после отправки этого последнего пакета, он получает еще один ACK:

 34 2.007883    CCC.CCC.CCC.CCC       sss.sss.sss.sss       TCP      60     57353 > 21500 [ACK] Seq=1 Ack=19305 Win=99999744 Len=0

И затем есть задержка почти ровно 250 мс, в течение которой сервер ничего не отправляет, прежде чем он получит это:

 35 2.257143    CCC.CCC.CCC.CCC       sss.sss.sss.sss       TCP      60     57353 > 21500 [ACK] Seq=1 Ack=22225 Win=99999744 Len=0
 36 2.257160    CCC.CCC.CCC.CCC       sss.sss.sss.sss       TCP      60     57353 > 21500 [ACK] Seq=1 Ack=24577 Win=99999744 Len=0

И затем продолжает отправку:

 37 2.257358    sss.sss.sss.sss       CCC.CCC.CCC.CCC       TCP      1514   21500 > 57353 [ACK] Seq=32769 Ack=1 Win=65536 Len=1460
 38 2.257362    sss.sss.sss.sss       CCC.CCC.CCC.CCC       TCP      1514   21500 > 57353 [ACK] Seq=34229 Ack=1 Win=65536 Len=1460
 39 2.257364    sss.sss.sss.sss       CCC.CCC.CCC.CCC       TCP      1514   21500 > 57353 [ACK] Seq=35689 Ack=1 Win=65536 Len=1460
 40 2.257365    sss.sss.sss.sss       CCC.CCC.CCC.CCC       TCP      1514   21500 > 57353 [ACK] Seq=37149 Ack=1 Win=65536 Len=1460

Здесь нужно отметить две очень интересные вещи.
Во-первых, сколько байтов было отправлено сервером, не дожидаясь ACK. Номер последней последовательности ACK, полученный сервером до этой задержки, равен Ack=19305, а номер последовательности последнего пакета, отправленного сервером в этот момент, равен Seq=30417.

Таким образом, во время этой паузы сервер отправил 11112 байт, которые еще не были подтверждены клиентом.

Во-вторых, это был один ACK, полученный сервером через мгновение после того, как он отправил кучу данных, что не побудило его отправить больше. Как будто этого ACK недостаточно.

ACK, полученный до этого, был Ack=16385, что дает 30417-16385=14032 байта, которые были отправлены сервером без подтверждения в этот момент. Только после получения ACK для последовательности № 24577, уменьшив это количество до 30417-24577=5840, сервер снова начал отправлять.

Таким образом, тот факт, что размер буфера 8 КБ велик по сравнению с эффективным размером окна 16 КБ, означает, что пропускная способность фактически несколько снижается, потому что сервер не будет отправлять ни один из блоков 8 КБ, пока не будет места для всего.

Наконец, для тех, кому интересно, есть опция TCP, называемая масштабированием окна, которая позволяет одному концу соединения объявить, что размер окна на самом деле несколько кратен числу в заголовке TCP. см. RFC 1323. Параметр передается в пакетах SYN, поэтому они не видны в середине соединения — есть только намек на то, что масштабирование окна действует, потому что заголовок TCP размера окна меньше, чем используемое окно.

person Michael Slade    schedule 24.04.2012
comment
Майкл, буфер отправки сервера для этой трассировки составлял 8 КБ (размер окна по умолчанию). Я считаю, что трассировка была захвачена на стороне клиента. Но вы правильно поняли: проблема в буфере SEND, который ограничивает окно. Я только что провел новый тест с большим окном отправителя на стороне сервера, и я ясно вижу гораздо более длинный пакет данных перед блокировкой. График в Wireshark ясно показывает, как окно перегрузки удваивается каждый раз при получении ACK. - person fabrizi0; 24.04.2012

Вы не можете установить размер приемного буфера> = 64 КБ после подключения сокета. Вы должны сделать это первым. В случае сервера это означает установку размера приемного буфера в сокете прослушивания: принятые сокеты наследуют его от сокета, из которого они приняты. Если вы этого не сделаете, параметр масштабирования окна TCP не может быть согласован, поэтому одноранговые узлы не смогут сообщить друг другу о размере более 64 КБ.

person user207421    schedule 24.04.2012

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

person supercat    schedule 23.04.2012
comment
Поверьте мне, буфер отправителя всегда полон. Мое приложение пишет максимально быстро. Это цикл for, который непрерывно вызывает write() буфера размером 8 КБ. - person fabrizi0; 24.04.2012

Размер окна приемника напрямую влияет на пропускную способность. Пропускная способность ‹= RWIN/RTT.

Несколько вещей также могут снизить пропускную способность. Установлены ли биты ECN в заголовке в 1? Знаете ли вы, есть ли потеря пакетов с обеих сторон? Похоже, что сервер истекает по таймауту. Можете ли вы распечатать порядковые номера входящих пакетов и исходящих ACK на стороне клиента и распечатать аналогичную информацию на стороне сервера. Если порядковый номер получателя равен 5 и он получает 6,7,8,9, он подтвердит ACK 6,7,8,9. Но если 6 потеряно, он подтвердит 5, когда получит пакеты 7,8,9. Порядковые номера могут раскрыть много информации.

Пауза в 250 мс кажется тайм-аутом.

Медленный старт

Алгоритм начинается в фазе экспоненциального роста первоначально с размером окна перегрузки (cwnd) в 1 или 2 сегмента и увеличивает его на 1 размер сегмента (SS) для каждого полученного ACK. Поскольку получатель обычно отправляет ACK для каждых двух сегментов, такое поведение фактически удваивает размер окна при каждом круговом обходе сети. Это поведение продолжается до тех пор, пока размер окна перегрузки (cwnd) не достигнет размера объявленного окна получателя или пока не произойдет потеря.

Может произойти следующее:
Сервер отправляет 1 пакет, получает 1 подтверждение
Сервер отправляет 2 пакета, получает 2 подтверждения (2,3)
Сервер отправляет 4 пакета, получает 4 подтверждения (4,5,6, 7)
Сервер отправляет 8 пакетов, получает 4 подтверждения (пакеты отбрасываются до их получения клиентом) (8,9,10,11)(тайм-аут 12)
Сервер отправляет 4 пакета, получает 4 подтверждения (12,13, 14,15)
Сервер отправляет 5 пакетов, получает 4 подтверждения (16,17,18,19)(время ожидания 20)
Сервер отправляет 3 пакета, получает 3 подтверждения (20,21,22)
Сервер отправляет 4 пакета, получает 4 подтверждения (23,24,25,26)
Сервер отправляет 5 пакетов, получает 4 подтверждения (27,28,29,30)(время ожидания 31)
Сервер продолжает работу 3,4,5 петля

person JustinDanielson    schedule 23.04.2012
comment
Джастин. Потери пакетов нет. Я убедился в этом. Я попытаюсь получить печатную трассировку с Wireshark и опубликовать ее здесь. Спасибо!!! - person fabrizi0; 24.04.2012
comment
Бит ECN может быть установлен маршрутизатором. Что бы смягчить скорость, с которой отправляются данные. Клиент может отправить 5 пакетов, а маршрутизатор устанавливает бит ECN или отбрасывает пакет. Бит ECN — это способ уведомить любую сторону, если сеть становится перегруженной. en.wikipedia.org/wiki/Explicit_Congestion_Notification - person JustinDanielson; 24.04.2012
comment
Если это сеть с высокой задержкой, вероятно, на пути будет много переходов. Окно получателя может быть таким же большим, как узкое место где-то посередине. Но если это узкое место отбрасывает пакеты, получатель никогда не узнает, что они были отправлены. Таким образом, он будет возвращать столько пакетов, сколько получил, в то время как сервер истечет время ожидания, ожидая получения, возможно, 10 подтверждений, а не 4, которые он получил. - person JustinDanielson; 24.04.2012
comment
Я могу попытаться повторно запустить тест и зафиксировать трассировку на обоих концах и попытаться вручную восстановить то, что происходит на самом деле... но это займет некоторое время. Какие-нибудь инструменты, которые вы могли бы порекомендовать, чтобы помочь мне отслеживать внутреннее состояние? - person fabrizi0; 24.04.2012
comment
Размер окна всего 16 бит. Я не понимаю, как вы могли отправить значение › 2^17-1. Попробуйте установить размер окна на 131071 или 65536 и посмотрите, как оно отреагирует. Можете ли вы использовать Wireshark на стороне сервера или нет? Не могли бы вы опубликовать еще несколько строк выше 21? Последовательность ACK 19305, 22225, 24577 наводит меня на мысль, что что-то было отброшено. Два раза передаются 19305 и 20765? - person JustinDanielson; 24.04.2012
comment
ACK никогда не отправляются для 20765 или 23685. - person JustinDanielson; 24.04.2012
comment
Я не знаю никаких инструментов, которые сделают это за вас. Вы можете вытащить данные в текстовый файл и отсортировать по отметкам времени. - person JustinDanielson; 24.04.2012
comment
Хорошо, RWIN может быть не более 65 КБ, но есть расширение RFC-1323, которое в основном позволяет передавать коэффициент масштабирования в пакете SYN в необязательном заголовке пакета TCP. Это в основном может расширить RWIN за пределы старых традиционных 65Kb. - person fabrizi0; 24.04.2012
comment
Клиент подтверждает до 24577 на пакете №36. Получателю не нужно подтверждать каждый отдельный пакет. Если клиент отправляет ACK # 24577, это означает, что он подтверждает все пакеты ДО # 24577. - person fabrizi0; 24.04.2012
comment
Но если бы не было потерь, не было бы сценария, в котором клиент пропускал бы порядковый номер. Разве клиент не отправляет ACK каждый раз, когда получает пакет. - person JustinDanielson; 24.04.2012
comment
Нет Джастина. Если клиент обнаруживает пропущенный пакет (просматривая порядковый номер), он повторно отправляет предыдущий ACK. Wireshark отмечает это как ACK как DUP предыдущего ACK. Клиент НЕ отправляет ACK для каждого полученного пакета. - person fabrizi0; 24.04.2012
comment
Хорошо... хм. Единственное, что я могу думать, это то, что буфер где-то на физическом уровне между клиентом и сервером заполняется. Это wireshark выше с точки зрения сервера или клиента? - person JustinDanielson; 24.04.2012