Вызов функции
setsockopt
приводит к одному из трех следующих сценариев в зависимости от значений двух элементов структуры
linger
.
1. Если
l_onoff
имеет нулевое значение, параметр выключается. Значение
l_linger
игнорируется и применяется ранее рассмотренный заданный по умолчанию сценарий TCP: функция
close
завершается немедленно.
2. Если значение
l_onoff
ненулевое, а
l_linger
равно нулю, TCP сбрасывает соединение, когда оно закрывается [128, с. 1019–1020], то есть TCP игнорирует все данные, остающиеся в буфере отправки сокета, и отправляет собеседнику сегмент RST, а не обычную последовательность завершения соединения, состоящую из четырех пакетов (см. раздел 2.5). Пример мы покажем в листинге 16.14. Тогда не наступает состояние TCP TIME_WAIT, но из-за этого возникает возможность создания другого воплощения (incarnation) этого соединения в течение 2MSL секунд (удвоенное максимальное время жизни сегмента). Оставшиеся старые дублированные сегменты из только что завершенного соединения могут быть доставлены новому воплощению, что приведет к ошибкам (см. раздел 2.6).
При указанных выше значениях
l_onoff
и
l_linger
SCTP также выполняет аварийное закрытие сокета, отправляя собеседнику пакет ABORT (см. раздел 9.2 [117]).
Отдельные выступления в Usenet звучат в защиту использования этой возможности, поскольку она позволяет избежать состояния TIME_WAIT и снова запустить прослушивающий сервер, даже если соединения все еще используются с известным портом сервера. Так не нужно делать, поскольку это может привести к искажению данных, как показано в RFC 1337 [11]. Вместо этого перед вызовом функции bind на стороне сервера всегда нужно использовать параметр сокета SO_REUSEADDR, как показано далее. Состояние TIME_WAIT — наш друг, так как оно предназначено для того, чтобы помочь нам дождаться, когда истечет время жизни в сети старых дублированных сегментов. Вместо того, чтобы пытаться избежать этого состояния, следует понять его назначение (см. раздел 2.6).
Тем не менее в некоторых обстоятельствах использование аварийного закрытия может быть оправдано. Одним из примеров является сервер терминалов RS-232, который может навечно зависнуть в состоянии CLOSE_WAIT, пытаясь доставить данные на забитый порт. Если же он получит сегмент RST, он сможет сбросить накопившиеся данные и заново инициализировать порт.
3. Если оба значения —
l_onoff
и
l_linger
— ненулевые, то при закрытии сокета ядро будет
close
, даже если время задержки ненулевое. При использовании этого свойства параметра
SO_LINGER
приложению важно проверить значение, возвращаемое функцией
close
. Если время ожидания истечет до того, как оставшиеся данные будут отправлены и подтверждены, функция
close
возвратит ошибку
EWOULDBLOCK
и все данные, оставшиеся в буфере отправки сокета, будут сброшены.
Теперь нам нужно точно определить, когда завершается функция
close
на сокете в различных сценариях, которые мы рассмотрели. Предполагается, что клиент записывает данные в сокет и вызывает функцию
close
. На рис. 7.1 показана ситуация по умолчанию.
Рис. 7.1. Действие функции close, заданное по умолчанию: немедленное завершение