Close vs shutdown socket?

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

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

Ответ 1

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

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

Ответ 2

Ни один из существующих ответов не говорит людям, как 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 будет отправлен на другую сторону, чтобы сообщить, что у нас больше нет данных для отправки.

Ответ 3

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

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

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

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

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

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

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

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

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

Ответ 4

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

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

Ответ 5

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

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

Ответ 6

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

Ответ 7

Закрыть

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

ShutDown

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

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

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

Ответ 8

в моем тесте.

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

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

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

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