В отличие от сокета UDP, TCP-соединение поддерживает значительный объем информации о состоянии, и благодаря этому состоянию некоторые операции могут выполняться асинхронно. Например, из-за управления потоком данные могут не быть отправленными немедленно при предоставлении их процессом.
Требование надежной доставки предполагает, что данные должны сохраняться после первой их передачи, чтобы при необходимости их можно было отправить повторно. Чтобы предотвратить зависание протокола в случае потери пакетов, каждое соединение поддерживает набор таймеров, использующихся для восстановления после потерь или сбоев другого узла.
Эти таймеры хранятся в управляющем блоке протокола для соединения. Ядро предоставляет службу таймеров через набор процедур callout(). Модуль TCP может зарегистрировать с помощью службы callout вплоть до пяти процедур тайм-аутов, как показано в таблице. У каждой процедуры имеется свое соответствующее время, когда она будет вызвана. В предыдущих версиях BSD тайм-ауты обрабатывались процедурой tcpslowtimo(), которая вызывалась каждые 500 миллисекунд, а затем при необходимости выполняла обработку таймера. Использование службы таймера ядра имеет двойную выгоду. Во-первых, оно является более точным, поскольку каждый таймер в управляющем блоке можно обрабатывать независимо, во-вторых, имеет меньшие издержки, поскольку процедуры не вызываются, если это не является абсолютно необходимым.
Для обработки вывода используются два таймера. Каждый раз при отправке данных по соединению запускается таймер повторной передачи (tcp_rexmt) путем вызова calloutreset(), если он уже не действует. Когда все новые данные подтверждены, таймер останавливается. Если таймер истекает, самые старые неподтвержденные данные отправляются повторно (по крайней мере, один полный пакет) и таймер запускается повторно с большим значением. Скорость, с которой увеличивается значение таймера (откат таймера), определяется по таблице коэффициентов, предусматривающей экспоненциальное увеличение значений тайм-аутов вплоть до максимума.
Другим таймером, использующимся для поддержания выходного потока, является таймер сохранения (persist timer) (tcp timer_persist()). Этот таймер защищает от другого вида потерь пакетов, которые могут вызвать затор соединения: потерю обновления окна, которое разрешило бы отправку дополнительных данных.
|
Процедура |
Тайм-аут |
Описание |
|
tcp_timer_2msl |
60 c |
Ожидание при закрытии |
|
tcp_timer_keep |
75 c |
Отправляет дежурное сообщение или удаляет бездействующее соединение |
|
tcp timer persist |
5-60 с |
Форсирует сохранение соединения |
|
tcp_timer_rexmt |
3 тика - 64 с |
Вызывается, когда нужна повторная передача |
|
tcp_timer_delack |
100 мс |
Отправляет другому узлу отложенное подтверждение |
Каждый раз, когда данные готовы к отправке, но окно отправки для этого слишком маленькое (нулевое или меньше приемлемого размера) и неподтвержденных данных нет (таймер повторной отправки не установлен), запускается таймер сохранения. Если до истечения времени таймера обновления окна не получено, процедура отправляет такой сегмент, который допускает окно отправки. Если этот размер равен нулю, она отправляет зондирование окна (единственный октет данных) и повторно запускает таймер сохранения в управляющем блоке.
Если обновление окна было потеряно в сети или если получатель проигнорировал отправку окна обновления, подтверждение будет содержать текущие сведения об окне. С другой стороны, если получатель по-прежнему не способен принимать дополнительные данные, он должен отправить подтверждение для предыдущих данных с по-прежнему закрытым окном. Закрытое окно могло бы существовать бесконечно; например, получатель мог бы быть клиентом сетевой регистрации, а пользователь мог остановить вывод терминала и уйти на обед (или в отпуск).
Третьим таймером, используемым TCP, является дежурный таймер. У дежурного таймера две различные цели на разных фазах соединения. В ходе установления соединения этот таймер ограничивает время для завершения трехстороннего рукопожатия. Если таймер истекает в ходе установления соединения, оно закрывается. После установления соединения дежурный таймер отслеживает бездействующие соединения, которые могли бы больше не существовать на другом узле вследствие разъединения сети или аварии.
Если установлена опция уровня сокета и соединение было бездействующим с момента последнего дежурного тайм-аута, процедура таймера отправит делсурный пакет (keepalive packet), имеющий целью получить от другого конца TCP либо подтверждение, либо сброс (RST). Если получен сброс, соединение будет закрыто; если после нескольких попыток не будет получено никакого ответа, соединение удаляется. Это средство спроектировано для того, чтобы сетевые серверы могли избегать «подвисших» соединений, когда клиент исчезает, не закрыв соединение. Дежурные пакеты не являются явной особенностью протокола TCP. Пакеты, использующиеся для этой цели FreeBSD, устанавливают номер последовательности на единицу меньше, чем snduna, который должен получить подтверждение от другого узла, если соединение по-прежнему существует.
Четвертый TCP-таймер известен как таймер 2MSL («двойного времени жизни сегмента»).
TCP запускает этот таймер, когда соединение завершено путем отправки подтверждения для FIN (из FIN_WAIT_2) или получено АСК для FIN (из состояния CLOSING, когда отправляющая сторона уже закрыта). При этих условиях отправитель не знает, было ли получено подтверждение. Если FIN отправляется повторно, желательно, чтобы осталось достаточное состояние, чтобы повторить подтверждение. Поэтому, когда ТСР-соединение входит в состояние TIMEWAIT, запускается таймер 2MSL; когда таймер истекает, управляющий блок удаляется. Если переданный повторно FIN получен, посылается другой АСК, и таймер запускается повторно. Чтобы предотвратить блокирование из-за этой задержки закрытия процессом соединения, любой запрос закрытия возвращается успешно без ожидания процессом таймера.
Таким образом, управляющий блок протокола может продолжать свое существование даже после того, как дескриптор сокета был закрыт. Кроме того, FreeBSD запускает таймер 2MSL, когда осуществляется вход в состояние FIN_WAIT_2 после закрытия пользователя; если соединение бездействует до истечения времени таймера, оно будет закрыто. Поскольку пользователь уже закрылся, через такое соединение в любом случае данные не могут быть приняты. Этот таймер устанавливается, поскольку некоторые другие реализации TCP (ошибочно) не отправляют FIN для только принимающего соединения. Соединения с такими хостами бесконечно оставались бы в состоянии FINWAIT2, если бы у системы не было тайм-аута.
Последним таймером является tcp_timer_delack(), который обрабатывает отложенные подтверждения.
- 30/01/2010 12:15 - Избегание синдрома незначительного окна
- 30/01/2010 11:55 - Отключение соединения в FreeBSD
- 30/01/2010 11:53 - Кеш SYN
- 24/01/2010 19:31 - MTU. Установление соединения
- 21/01/2010 09:34 - Оценка времени обращения
- 16/01/2010 11:38 - Отметка времени. Номер последовательности. Переменные последовательности
- 16/01/2010 11:25 - SYN. Состояния ТСР-соединения
- 16/01/2010 11:03 - Процедура пересылки IP-пакетов
- 16/01/2010 10:58 - Процедура ввода IPv4. Входная процедура.
- 16/01/2010 10:46 - Размер пакета IP. Выходная процедура IP.