Полуоткрытые соединения и другие аномалии

Созданное соединение называют "полуоткрытым", если один из TCP закрыл или прервал соединение на своем конце в одностороннем порядке, или если два конца соединения стали десинхронизированными в связи с аварийным отключением, которое привело к потере памяти. Такие соединения будут автоматически восстанавливаться при пытке послать данные в любом направлении. Однако полуоткрытые соединения считаются аномальными, и постепенно включается процедура восстановления. Если соединение узла А больше не существует, то попытка пользователя узла В послать ему какие-либо данные приведет к тому, что TCP узла В получит управляющее сообщение сброса в исходное состояние (reset). Такое сообщение указывает TCP узла В на ошибку, и ожидается прерывание соединения.
Предположим, что два пользовательских процесса А и В общаются друг с другом, когда происходит авария, вызывая потерю памяти TCP процесса А. В зависимости от операционной системы, поддерживающей TCP процесса А, скорее всего, существует некоторый механизм восстановления ошибок. Когда TCP снова включен, А начнет соединение заново или с точки восстановления. В результате А будет пытаться снова открыть (OPEN) соединение или послать (SEND) в соединение, которое он считает открытым. В последнем случае он получает от локального (A) TCP сообщение "соединение не открыто". При попытке создать соединение TCP процесса А пошлет сегмент, содержащий SYN.

Генерация команды сброса

Как общее правило, команда сброса (RST) должна посылаться всякий раз, когда приходит сегмент, который, очевидно, не предназначен для текущего соединения. Команда сброса не должна посылаться, если неясно, что это именно тот случай. Ниже описаны три существующие группы состояний.
1. Если соединение не существует (CLOSED), то команда сброса (reset) посылается в ответ на любой входящий сегмент, за исключением другой команды сброса. В частности, SYN, адресованный несуществующему соединению, отбрасывается этими средствами. Если входящий сегмент имеет поле АСК, то команда сброса берет свой номер последовательности из поля сегмента АСК, иначе команда сброса имеет номер последовательности равный нулю, а поле АСК задается как сумма номера последовательности и длины входящего сегмента. Соединение остается в состоянии CLOSED.
2. Если соединение находится в любом не синхронизированном состоянии (LISTEN, SYN-SENT, SYN-RECEIVED), и входящий сегмент подтверждает что-то еще не посланное (сегмент несет неприемлемое АСК), или если входящий сегмент имеет уровень защиты или ячейку, которые не точно соответствуют уровню и ячейке, запрошенным соединением, то посылается команда сброса. Если посланный SYN еще не был подтвержден и уровень приоритета входящего сегмента выше, чем запрошенный уровень приоритета, то либо поднимается уровень локального приоритета (если допускается пользователем и системой), либо посылается команда сброса. Если уровень приоритета входящего сегмента меньше, чем запрошенный уровень приоритета, то процесс продолжается, как если бы приоритет соответствовал точно (если удаленный TCP не может поднять уровень приоритета, чтобы соответствовать точно, это будет обнаружено в следующем сегменте, который он посылает, и соединение будет прекращено). Если посланный сигнал SYN был подтвержден (возможно, в этом входящем сегменте), то уровень приоритета входящего сегмента должен точно соответствовать уровню локального приоритета. Если это не так, должна быть послана команда сброса. Если входящий сегмент имеет поле АСК, то команда сброса берет номер последовательности из поля сегмента АСК; иначе команда сброса имеет номер последовательности ноль, а поле АСК задается как сумма номера последовательности и длины входящего сегмента. Соединение остается в том же состоянии.
3. Если соединение находится в синхронизированном состоянии (ESTABLISHED, FIN-WAIT-1, FIN-WAIT-2, CLOSE-WAIT, CLOSING, LASTACK, TIME-WAIT), то любой неприемлемый сегмент (номер последовательности вне окна или неприемлемый номер подтверждения) должен извлекать только пустой сегмент подтверждения, содержащий текущий посланный номер последовательности и подтверждение, указывающее следующий ожидаемый к получению номер последовательности, и соединение остается в том же состоянии. Если входящий сегмент имеет уровень защиты, ячейку или приоритет, которые не точно соответствуют уровню, ячейке и приоритету, запрошенным для соединения, посылается команда сброса и соединение переходит в состояние CLOSED. Команда сброса берет свой номер последовательности из поля АСК входящего сегмента.

Обработка команды сброса

Во всех состояниях, кроме SYN-SENT, все сегменты сброса (RST) контролируются проверкой их полей SEQ. Сброс будет признан действительным, если его номер последовательности находится в пределах окна. В состоянии SYN-SENT (RST получен в ответ на начальный SYN) RST является приемлемым, если поле АСК подтверждает SYN.
Получатель RST сначала проверяет его, а затем изменяет состояние. Если получатель был в состоянии LISTEN, то он его игнорирует. Если получатель был в состоянии SYN-RECErVED, а ранее — в состоянии LISTEN, то получатель возвращается в состояние LISTEN; иначе получатель прерывает соединение и переходит в состояние CLOSED. Если получатель был в другом состоянии, он прерывает соединение, уведомляет пользователя и переходит в состояние CLOSED. Закрытие соединения (CLOSE) является операцией, означающей: "У меня больше нет данных для пересылки". Понятие закрытия дуплексного соединения часто неверно интерпретируется, так как может быть не очевидно, как интерпретировать получающую сторону соединения. Мы выбрали одностороннюю интерпретацию CLOSE. Пользователи, которые делают CLOSE, могут продолжать RECEIVE (получать), пока они не получат сообщение о том, что другая сторона также CLOSED (закрыта). Таким образом, программа могла бы инициировать несколько команд SEND (послать) после команды CLOSE (закрыть), и затем продолжить получать, пока не будет получен сигнал, что команда RECEIVE (получить) не сработала, так как другая сторона закрылась (CLOSED). Мы предполагаем, что TCP будет сигнализировать пользователю, даже если нет никаких ожидающих RECEIVE и другая сторона закрыта, поэтому пользователь сможет аккуратно завершить свою работу. TCP надежно доставит все буферы, посланные до того, как соединение было закрыто, поэтому пользователю, который не ожидает возврата никаких данных, необходимо только подождать сообщения, что соединение успешно закрыто, следовательно, все его данные были получены TCP местом назначения. Пользователи должны продолжать считывание соединений, которые они закрыли для отправки, пока TCP не сообщит об отсутствии данных. Рассмотрим три существующих сценария:
1. Закрытие инициирует пользователь, приказывая TCP закрыть соединение.
2. Закрытие инициирует удаленный TCP, посылая управляющий сигнал FIN.
3. Оба пользователя закрывают соединение одновременно.
Сценарий 1: локальный пользователь инициирует закрытие
В этом случае может быть создан сегмент FIN и помещен в очередь исходящих сегментов. Никакие другие команды SEND пользователя TCP не принимает и входит в состояние FIN-WAIT-1. В этом состоянии допускаются команды RECEIVE (получить). Все сегменты, предшествующие и включающие FIN, будут посылаться повторно, пока не будет подтверждено их получение. Когда другой TCP подтвердит FIN и пошлет свою собственную команду FIN, первый TCP может подтвердить (АСК) этот FIN. Отметим, что TCP, получающий FIN, будет подтверждать (АСК), но не посылать свой собственный FIN, пока его пользователь также не закроет соединение.
Сценарий 2: TCP получает FIN из сети
Если из сети прибывает не вынужденный FIN, то получающий TCP может подтвердить его и сообщить пользователю, что соединение закрывается. Пользователь ответит командой CLOSE, затем TCP может послать FIN другому TCP после отправки всех оставшихся данных. Затем TCP ожидает, пока его собственный FIN не будет подтвержден, после чего удаляет соединение. Если подтверждение АСК не поступает после периода ожидания пользователя, соединение прерывается и об этом сообщается пользователю.
Сценарий 3: оба пользователя закрывают соединение одновременно
Одновременное закрытие пользователями на обоих концах соединения вызывает обмен сегментами FIN. Когда все сегменты, предшествующие FIN, обработаны и подтверждены, каждый TCP может подтвердить полученный FIN. После получения этих АСК оба удалят соединение.

Обмен срочной информацией

Цель механизма срочной доставки TCP состоит в том, чтобы посылающий пользователь мог стимулировать получателя принять некоторые срочные данные и разрешить получающему TCP указать получателю, что все известные в данный момент срочные данные получены пользователем.
Этот механизм позволяет в потоке данных обозначить точку как конец срочной информации. Всякий раз, когда эта точка у получающего TCP возникает перед номером получаемой последовательности (RCV.NXT), TCP должен приказать пользователю перейти в "режим срочности". Когда номер получаемой последовательности сталкивается с указателем срочности, TCP должен приказать пользователю перейти в "нормальный режим". Если указатель срочности обновляется, когда пользователь находится в "режиме срочности", обновление будет для пользователя невидимым. Метод использует поле срочности, которое переносится во всех передаваемых сегментах. Управляющий флаг URG указывает, что поле срочности имеет смысл и должно быть добавлено к номеру последовательности сегмента, чтобы породить указатель срочности. Отсутствие этого флага указывает, что ожидающих срочных данных нет.
Чтобы послать указание о срочности, пользователь должен послать также по крайней мере один октет данных. Если посылающий пользователь указывает путь доступа, то своевременность доставки срочной информации в процесс места назначения улучшается.

Управление окном

Окно, посылаемое в каждом сегменте, указывает диапазон номеров последовательности, которые готов принять в данный момент отправитель окна (получатель данных). Существует допущение, что это связано с доступным в этот момент пространством буфера данных для соединения.
Указание большого окна ускоряет передачу. Если поступает больше данных, чем может быть принято, они будут отбрасываться. Это приведет к излишним повторным передачам, создающим дополнительную нагрузку на сеть и TCP. Указание маленького окна может ограничить передачу данных до появления задержки прохода туда и обратно перед передачей каждого нового сегмента.
Предоставленные механизмы позволяют TCP объявлять большое окно, а потом сообщать, что оно значительно меньше, не принимая слишком много данных. Это так называемое "сжатие окна", очень обескураживает. Принцип надежности диктует, что TCP не будет сжимать окно самостоятельно, но будет готов к такому поведение со стороны другого TCP.
Посылающий TCP должен быть готов принять от пользователя и послать по крайней мере один октет новых данных, даже если окно отправки равно нулю. Посылающий TCP должен регулярно повторно посылать кадры получающему TCP, даже когда окно равно нулю. Для интервала повторной передачи рекомендуется использовать две минуты, когда размер окна равен нулю. Эта повторная передача является существенной для гарантии, что в том случае, когда оба TCP имеют нулевое окно, повторное открытие окна будет обязательно сообщено другому.
Если сегмент прибывает, когда получающий TCP имеет нулевое окно, он все равно должен послать подтверждение, показывающее его следующий ожидаемый номер последовательности и текущее окно (ноль). Посылающий TCP упаковывает данные для передачи в сегменты, которые соответствуют текущему окну, и может перепаковывать сегменты в очереди повторной передачи. Такая перепаковка не обязательна, но может оказаться полезной.
При соединении с однонаправленным потоком данных информация об окне будет переноситься в сегментах подтверждения, которые все имеют одинаковый номер, поэтому не существует способа переупорядочить их, если они приходят в беспорядке. Это не является серьезной, но позволит информации окна при случае временно основываться на старых отчетах получателя данных. Избежать этой проблемы можно, действуя на основе информации окна из сегментов, которые несут самые большие номера подтверждения (т.е. сегменты с номером подтверждения, равным или большим, чем самый большой из полученных ранее).
Процедура управления окном имеет существенное влияние на производительность коммуникации. Следующие комментарии являются предложениями для реализации.
Предложения по управлению окном Выделение очень маленького окна приводит к тому, что данные будут передаваться во множестве мелких сегментов, в то время как лучшая производительность достигается с помощью меньшего числа больших сегментов.
Одним из предложений по избавлению от маленьких окон для получателя является задержка обновления окна, пока дополнительное выделение не составит по крайней мере X процентов максимально возможного выделения для соединения (где X может быть от 20 до 40).
Другое предложение для отправителя с целью избежать отправки маленьких сегментов состоит в ожидании, пока окно не станет достаточно большим, прежде чем посылать данные. Если пользователь дает сигнал функции push, то данные должны быть отправлены, даже если это маленький сегмент.
Отметим, что подтверждения не должны задерживаться, так как это может привести к ненужным повторным передачам. Одной из стратегий является отправка подтверждения, когда прибывает маленький сегмент (не обновляя информацию об окне), и затем отправка другого подтверждения с новой информацией об окне, когда окно станет больше. Сегмент, посылаемый для проверки нулевого окна, может также начать разбиение передаваемых данных в сегменты все меньшего размера. Если сегмент, содержащий единственный октет данных, посланный для проверки нулевого окна, принимается, он поглощает один октет доступного в данный момент окна.
Если посылающий TCP просто посылает столько, сколько может, когда окно ненулевое, то передаваемые данные будут разбиты на чередующиеся большие и маленькие сегменты. Со временем случайные паузы у получателя, делающие доступным распределение окна, приведут к разбиению больших сегментов на более мелкие. Через какое-то время передача данных будет осуществляться в основном маленькими сегментами.
Суть вышеизложенного в том, что реализации TCP активно пытаются комбинировать выделение маленьких окон с большими окнами, так как механизмы управления окном ведут к появлению множества маленьких окон в простейших реализациях.
Интерфейс пользователь/ТСР
Следующее ниже функциональное описание команд TCP является в ка-кой-го степени общим, однако весь TCP должен предоставить некоторый минимальный набор служб для гарантии, что все реализации TCP могут поддерживать одну и ту же иерархию протоколов. Этот раздел определяет функциональные интерфейсы, встречающиеся во всех реализациях TCP.