Этот параметр определяет, как работает функция close
для протоколов, ориентированных на установление соединения (например, TCP и SCTP, но не UDP). По умолчанию функция close
возвращает управление немедленно, но если в отправляющем буфере сокета остаются какие-либо данные, система попытается доставить данные собеседнику.
Параметр сокета SO_LINGER
позволяет нам изменять поведение по умолчанию. Для этого необходимо, чтобы между пользовательским процессом и ядром была передана следующая структура, определяемая в заголовочном файле :
struct linger {
int l_onoff; /* 0=off, ненулевое значение=on */ int l_linger;
/* время ожидания, в POSIX измеряется в секундах */
};
Вызов функции 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
— ненулевые, то при закрытии сокета ядро будет ждать ( linger ) [128, с. 472]. То есть если в буфере отправки сокета еще имеются какие-либо данные, процесс входит в состояние ожидания до тех пор, пока либо все данные не будут отправлены и подтверждены другим концом TCP, либо не истечет время ожидания. Если сокет был установлен как неблокируемый (см. главу 16), он не будет ждать завершения выполнения функции close
, даже если время задержки ненулевое. При использовании этого свойства параметра SO_LINGER
приложению важно проверить значение, возвращаемое функцией close
. Если время ожидания истечет до того, как оставшиеся данные будут отправлены и подтверждены, функция close
возвратит ошибку EWOULDBLOCK
и все данные, оставшиеся в буфере отправки сокета, будут сброшены.
Теперь нам нужно точно определить, когда завершается функция close
на сокете в различных сценариях, которые мы рассмотрели. Предполагается, что клиент записывает данные в сокет и вызывает функцию close
. На рис. 7.1 показана ситуация по умолчанию.
Рис. 7.1. Действие функции close, заданное по умолчанию: немедленное завершение
Мы предполагаем, что когда приходят данные клиента, сервер временно занят. Поэтому данные добавляются в приемный буфер сокета его протоколом TCP. Аналогично, следующий сегмент (сегмент FIN клиента) также добавляется к приемному буферу сокета (каким бы образом реализация ни сохраняла сегмент FIN). Но по умолчанию клиентская функция close
сразу же завершается. Как мы показываем в этом сценарии, клиентская функция close
может завершиться перед тем, как сервер прочитает оставшиеся данные в приемном буфере его сокета. Если узел сервера выйдет из строя перед тем, как приложение-сервер считает оставшиеся данные, клиентское приложение никогда об этом не узнает.
Читать дальше
Конец ознакомительного отрывка
Купить книгу