закрыть сокет vs shutdown?

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

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

8 ответов


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

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


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

стандартное TCP-соединение завершается 4-way finalization:

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

однако есть еще один "эмерджентный" способ закрыть TCP-соединение:

  1. участник отправляет первый пакет и отказывается от соединения
  2. другая сторона получает первый, а затем отказаться от соединения, а также

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

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


есть некоторые ограничения с 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 Дальнейшие отправки и получения запрещены


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

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


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

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


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


закрыть

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

остановка

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

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

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


в моем тесте.

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

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

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

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