На илл. 6.14 (а) показан нормальный случай, при котором пользователь отправляет DR (DISCONNECTION REQUEST), чтобы инициировать разрыв соединения. Когда запрос доставляется, получатель также отвечает сегментом DR и включает таймер на случай его потери. Когда запрос прибывает, первый пользователь отправляет в ответ на него сегмент ACK и разрывает соединение. Наконец, когда ACK приходит, получатель также разрывает соединение. Разъединение означает, что транспортная подсистема удаляет информацию об этой связи из своей таблицы открытых соединений и сигнализирует о разрыве владельцу соединения (потребителю транспортной службы). Эта процедура отличается от применения пользователем примитива DISCONNECT.
Если последний сегмент ACK теряется (илл. 6.14 (б)), ситуацию спасает таймер. Когда время истекает, соединение разрывается в любом случае.
Теперь рассмотрим случай потери второго DR. Пользователь, инициировавший разъединение, не получит ожидаемого ответа, у него истечет время ожидания, и он начнет все сначала. На илл. 6.14 (в) показано, как это происходит, если все последующие запросы и подтверждения успешно доходят до адресатов.
Последний сценарий (илл. 6.14 (г)) аналогичен предыдущему с одной лишь разницей: в этом случае предполагается, что все повторные попытки передать DR также терпят неудачу, поскольку все сегменты теряются. После N попыток отправитель, наконец, сдается и разрывает соединение. Тем временем у получателя также истекает время, и он тоже отсоединяется.
Хотя такого протокола обычно бывает вполне достаточно, теоретически он может ошибиться, если потеряются изначальный DR и все N повторных передач. Отправитель сдается и разрывает соединение, тогда как другая сторона ничего не знает о попытках разорвать связь и сохраняет активность. В результате получается полуоткрытое соединение, что недопустимо.
Этой ситуации можно избежать, если не позволить отправителю сдаваться после N повторных передач, а заставить его продолжать попытки, пока не будет получен ответ. Однако если другой стороне будет разрешено разрывать связь по таймеру, тогда отправитель действительно будет вечно повторять попытки, так как ответа он не получит никогда. Если же получателю также не разрешать разрывать соединение по таймеру, то протокол зависнет в ситуации, изображенной на илл. 6.14 (г).
Илл. 6.14. Четыре сценария разрыва соединения. (а) Нормальный случай «тройного рукопожатия». (б) Потеряно последнее подтверждение. (в) Потерян ответ. (г) Потерян ответ и последующие запросы разъединения
Чтобы избавиться от полуоткрытых соединений, используется правило, гласящее, что если по соединению в течение определенного времени не приходит ни одного сегмента, оно автоматически разрывается. Таким образом, если одна сторона разорвет соединение, другая обнаружит отсутствие активности и также отсоединится. Правило работает и в тех случаях, когда соединение разрывается не по инициативе одной из сторон, а по причине невозможности передачи пакетов между этими хостами в сети.
Для реализации этого правила каждая сторона должна иметь таймер, который перезапускается после отправки каждого сегмента. Если таймер срабатывает, передается пустой сегмент — чтобы другая сторона не отсоединилась. Но если применяется правило автоматического разъединения и передается слишком много пустых сегментов подряд, только чтобы линия не простаивала, то соединение автоматически разрывается сначала одной стороной, а затем и другой.
На этом мы заканчиваем обсуждение данного вопроса, но теперь должно быть ясно, что разорвать соединение без потери данных не так просто, как это кажется на первый взгляд. Из всего сказанного можно сделать вывод, что пользователь службы должен принимать участие в решении вопроса о разъединении — транспортная подсистема не может с этим справиться самостоятельно. Обратите внимание, что хотя TCP обычно использует симметричный разрыв связи (при этом каждая сторона независимо прерывает свое соединение, отправляя пакет FIN после окончания передачи данных), веб-серверы часто передают клиентам специальный пакет RST, сообщающий о мгновенном разрыве соединения, что больше похоже на асимметричный разрыв. Это возможно только потому, что веб-сервер знаком с процедурой обмена данными. Сначала он получает запрос от клиента (единственное, что отправляет клиент), а затем отсылает ему ответ.
После отправки ответа обмен данными завершен: каждая сторона передала другой то, что требовалось. Поэтому сервер может резко разорвать соединение, отправив клиенту предупреждение. Получив его, клиент сразу же разорвет соединение. Если предупреждение не дойдет до клиента, он через какое-то время поймет, что сервер с ним больше не общается, и отсоединится. В любом случае данные будут успешно переданы.
6.2.4. Контроль ошибок и управление потоком данных