закрыть против отключения сокета? - PullRequest
191 голосов
/ 12 ноября 2010

В C я понял, что если мы закроем сокет, это означает, что сокет будет разрушен и может быть использован позже.

Как насчет выключения?В описании сказано, что он закрывает половину дуплексного соединения с этим сокетом.Но будет ли этот сокет уничтожен как системный вызов close?

Ответы [ 8 ]

175 голосов
/ 12 ноября 2010

Это объяснено в сетевом руководстве Биджа. shutdown - это гибкий способ блокировать общение в одном или обоих направлениях. Когда вторым параметром является SHUT_RDWR, он блокирует отправку и получение (например, close). Однако close - это способ уничтожить сокет.

С помощью shutdown вы по-прежнему сможете получать ожидающие данные, которые уже отправил узел (спасибо Джои Адамсу за это).

116 голосов
/ 06 мая 2014

Ни один из существующих ответов не говорит людям, как shutdown и close работают на уровне протокола TCP, поэтому стоит добавить это.

Стандартное TCP-соединение прерывается при 4-сторонней финализации:

  1. Когда у участника больше нет данных для отправки, он отправляет пакет FIN другому
  2. Другая сторона возвращает ACK для FIN.
  3. Когда другая сторона также закончила передачу данных, она отправляет еще один пакет FIN
  4. Первоначальный участник возвращает ACK и завершает передачу.

Однако существует другой «эмерджентный» способ закрытьTCP-соединение:

  1. Участник отправляет RST-пакет и прекращает соединение
  2. Другая сторона получает RST и затем также прекращает соединение

В моем тесте с Wireshark с параметрами сокетов по умолчанию shutdown отправляет пакет FIN на другой конец, но это все, что он делает.Пока другая сторона не отправит вам пакет FIN, вы все равно сможете получать данные.Как только это произойдет, ваш Receive получит результат 0 размера.Поэтому, если вы первый, кто отключил «отправить», вы должны закрыть сокет, как только закончите получать данные.

С другой стороны, если вы позвоните close, пока соединение все еще активно (другая сторона все еще активна, и вы можете иметь неотправленные данные также в системном буфере), пакет RST будет отправлен на другую сторону.Это хорошо для ошибок.Например, если вы считаете, что другая сторона предоставила неверные данные или отказалась предоставить данные (атака DOS?), Вы можете сразу же закрыть сокет.

Мое мнение о правилах будет следующим:

  1. Если возможно, рассмотрите shutdown до close
  2. Если вы закончили прием (получены данные 0 размера) до того, как решили завершить работу, закройте соединение после завершения последней отправки (если есть).
  3. Если вы хотите нормально закрыть соединение, завершите соединение (с помощью SHUT_WR и, если вас не волнует получение данных после этой точки, а также с SHUT_RD), и подождите, пока не получите данные размера 0и затем закройте сокет.
  4. В любом случае, если возникла какая-либо другая ошибка (например, тайм-аут), просто закройте сокет.

Идеальные реализации для SHUT_RD иSHUT_WR

Следующие не были проверены, доверяйте на свой страх и риск.Тем не менее, я считаю, что это разумный и практичный способ действий.

Если стек TCP получает отключение только с помощью SHUT_RD, он должен пометить это соединение как ожидание данных.Любые ожидающие и последующие read запросы (независимо от того, в каком потоке они находятся) будут возвращены с нулевым результатом.Тем не менее, соединение все еще активно и доступно - вы все еще можете получать данные OOB, например.Кроме того, ОС будет отбрасывать любые данные, которые она получает для этого подключения.Но это все, никакие пакеты не будут отправлены на другую сторону.

Если стек TCP получает завершение только с SHUT_WR, он должен пометить это соединение, так как больше данных отправлять нельзя.Все ожидающие запросы на запись будут завершены, но последующие запросы на запись не будут выполнены.Кроме того, пакет FIN будет отправлен другой стороне, чтобы сообщить им, что у нас нет больше данных для отправки.

35 голосов
/ 12 ноября 2010

С close() есть некоторые ограничения, которых можно избежать, если вместо них использовать shutdown().

close() прервет оба направления в соединении TCP. Иногда вы хотите сообщить другой конечной точке, что вы закончили отправку данных, но все же хотите получать данные.

close() уменьшает счетчик ссылок дескрипторов (поддерживается в записи таблицы файлов и подсчитывает количество открытых в данный момент дескрипторов, ссылающихся на файл / сокет) и не закрывает сокет / файл, если дескриптор не равен 0. Это означает, что что если вы разветвляетесь, очистка происходит только после того, как счетчик ссылок уменьшится до 0. С shutdown() можно инициировать нормальную последовательность закрытия TCP, игнорируя счетчик ссылок.

Параметры следующие:

int shutdown(int s, int how); // s is socket descriptor

int how может быть:

SHUT_RD или 0 Дальнейшие получения запрещены

SHUT_WR или 1 Дальнейшие посылки запрещены

SHUT_RDWR или 2 Дальнейшие отправки и получения запрещены

15 голосов
/ 12 ноября 2010

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

Таким образом, используйте shutdown, чтобы отправить последовательность завершения работы на уровне TCP, и используйте close, чтобы высвободить ресурсы, используемые структурами данных сокетов в вашем процессе. Если вы не выдавали явную последовательность выключения к тому времени, когда вы вызываете close, то она инициируется для вас.

7 голосов
/ 12 ноября 2014

"shutdown () на самом деле не закрывает дескриптор файла - он просто меняет его удобство использования. Чтобы освободить дескриптор сокета, вам нужно использовать close ()." 1

7 голосов
/ 15 ноября 2010

Я также добился успеха в Linux, используя shutdown() из одного pthread, чтобы заставить другой pthread, заблокированный в connect(), прервать работу раньше.

В других ОС (по крайней мере, в OSX) я обнаружил, что вызова close() было достаточно, чтобы получить connect() сбой.

3 голосов
/ 07 июля 2016

Закрыть

Когда вы закончили использовать сокет, вы можете просто закрыть его файловый дескриптор с помощью close;Если все еще есть данные, ожидающие передачи по соединению, обычно закрыть пытается завершить эту передачу.Вы можете управлять этим поведением, используя опцию сокета SO_LINGER, чтобы указать период ожидания;см. Параметры сокета.

ShutDown

Вы также можете отключить только прием или передачу по соединению, вызывая shutdown.

Функция shutdown отключает соединение сокета,Его аргумент how указывает, какое действие нужно выполнить: 0 Прекратить получение данных для этого сокета.Если поступят дополнительные данные, отклоните их.1 Перестаньте пытаться передавать данные из этого сокета.Откажитесь от любых данных, ожидающих отправки.Прекратить поиск подтверждения уже отправленных данных;не передавайте его повторно, если оно потеряно.2 Остановите прием и передачу.

Возвращаемое значение равно 0 в случае успеха и -1 при ошибке.

1 голос
/ 17 сентября 2015

в моем тесте.

close отправит пакет fin и немедленно уничтожит fd, если сокет не используется другими процессами

shutdown SHUT_RD , процесс все еще может получать данные из сокета, но recv вернет 0, если буфер TCP пуст. После того, как узел отправит больше данных, recv вернет данные снова.

shutdown SHUT_WR отправит пакет fin, чтобы указать, что дальнейшие отправки запрещены. узел может получить данные, но он получит 0, если его буфер TCP пуст

shutdown SHUT_RDWR (равнозначно использованию SHUT_RD и SHUT_WR ) отправит первый пакет, если узел отправит больше данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...