Корректное завершение работы, параметры задержки и закрытие сокета

Следующий материал предоставляется в качестве пояснения к вопросу завершения подключений сокетов, закрывающих сокеты. Важно различать разницу между завершением подключения к сокету и закрытием сокета.

Завершение подключения к сокету включает обмен сообщениями протокола между двумя конечными точками, что далее называется последовательностью завершения работы. Определены два общих класса последовательностей завершения работы: корректное и прерванное (также называемое жестким). В корректной последовательности завершения работы все данные, которые были поставлены в очередь, но еще не переданы, могут быть отправлены до закрытия подключения. При прерванном завершении работы все неотправленные данные теряются. Вхождение последовательности завершения работы (корректное или прерванное) также можно использовать для предоставления FD_CLOSE указания связанным приложениям о том, что завершение работы выполняется.

Закрытие сокета, с другой стороны, приводит к освобождению дескриптора сокета, поэтому приложение больше не может ссылаться на сокет или использовать его каким-либо образом.

В сокетах Windows функция завершения работы и функция WSASendDisconnect можно использовать для запуска последовательности завершения работы, а функция closesocket используется для освобождения дескрипторов сокетов и освобождения связанных ресурсов. Однако некоторая путаница возникает из-за того, что функция closesocket неявно вызывает последовательность завершения работы, если она еще не произошла. На самом деле, это довольно распространенная практика программирования, чтобы полагаться на эту функцию и использовать closesocket как для запуска последовательности завершения работы, так и для освобождения дескриптора сокета.

Чтобы упростить это использование, интерфейс сокетов предоставляет элементы управления с помощью механизма параметра сокета, который позволяет программисту указать, должна ли последовательность неявного завершения работы быть корректной или прерванной, а также должна ли функция closesocket задерживаться (которая не завершается немедленно), чтобы дать время для завершения последовательности корректного завершения работы. Эти важные различия и последствия использования closesocket таким образом до сих пор не изучены широко.

Установив соответствующие значения для параметров сокета SO_LINGER и SO_DONTLINGER, с помощью функции closesocket можно получить следующие типы поведения:

  • Последовательность прерывания работы, немедленное возвращение из closesocket.
  • Корректное завершение работы с задержкой возврата до завершения последовательности завершения работы или до истечения указанного интервала времени. Если интервал времени истекает до завершения последовательности корректного завершения работы, происходит прерывание последовательности завершения работы и функция closesocket возвращается.
  • Корректное завершение работы, немедленное возвращение— позволяет завершить последовательность завершения работы в фоновом режиме. Хотя это поведение является поведением по умолчанию, приложение не может узнать, когда (или ли) последовательность корректного завершения работы фактически завершается.

Использование SO_LINGER и SO_DONTLINGER параметров сокета и связанной структуры задерживающегося сокета более подробно рассматривается в справочных разделах по SOL_SOCKET Параметры сокета и структура затяжного сокета .

Один из способов, который можно использовать для минимизации вероятности возникновения проблем во время разрыва подключения, заключается в том, чтобы не полагаться на неявное завершение работы, инициированное closesocket. Вместо этого используйте одну из двух явных функций завершения работы: shutdown или WSASendDisconnect. Это, в свою очередь, приводит к получению одноранговым приложением FD_CLOSE указания о том, что получены все ожидающие данные. Чтобы проиллюстрировать это, в следующей таблице показаны функции, которые будут вызываться клиентским и серверным компонентами приложения, где клиент отвечает за инициацию корректного завершения работы.

На стороне клиента На сервере
(1) Вызывает завершение работы, SD_SEND, чтобы сообщить об окончании сеанса, и у этого клиента больше нет данных для отправки.
(2) Получает FD_CLOSE, указывающие на корректное завершение работы и получение всех данных.
(3) Отправляет все оставшиеся данные ответа.
(только локальное значение времени) Получает FD_READ и вызывает recv для получения любых данных ответа, отправленных сервером . (4) Вызывает завершение работы, SD_SEND, чтобы указать, что сервер больше не имеет данных для отправки.
(5) Получает FD_CLOSE указание. (только локальное значение времени) Вызывает closesocket .
(6) Вызывает closesocket.