Ни один из существующих ответов не говорит людям, как shutdown
и close
работают на уровне протокола TCP, поэтому стоит добавить это.
Стандартное TCP-соединение прерывается при 4-сторонней финализации:
- Когда у участника больше нет данных для отправки, он отправляет пакет FIN другому
- Другая сторона возвращает ACK для FIN.
- Когда другая сторона также закончила передачу данных, она отправляет еще один пакет FIN
- Первоначальный участник возвращает ACK и завершает передачу.
Однако существует другой «эмерджентный» способ закрытьTCP-соединение:
- Участник отправляет RST-пакет и прекращает соединение
- Другая сторона получает RST и затем также прекращает соединение
В моем тесте с Wireshark с параметрами сокетов по умолчанию shutdown
отправляет пакет FIN на другой конец, но это все, что он делает.Пока другая сторона не отправит вам пакет FIN, вы все равно сможете получать данные.Как только это произойдет, ваш Receive
получит результат 0 размера.Поэтому, если вы первый, кто отключил «отправить», вы должны закрыть сокет, как только закончите получать данные.
С другой стороны, если вы позвоните close
, пока соединение все еще активно (другая сторона все еще активна, и вы можете иметь неотправленные данные также в системном буфере), пакет RST будет отправлен на другую сторону.Это хорошо для ошибок.Например, если вы считаете, что другая сторона предоставила неверные данные или отказалась предоставить данные (атака DOS?), Вы можете сразу же закрыть сокет.
Мое мнение о правилах будет следующим:
- Если возможно, рассмотрите
shutdown
до close
- Если вы закончили прием (получены данные 0 размера) до того, как решили завершить работу, закройте соединение после завершения последней отправки (если есть).
- Если вы хотите нормально закрыть соединение, завершите соединение (с помощью SHUT_WR и, если вас не волнует получение данных после этой точки, а также с SHUT_RD), и подождите, пока не получите данные размера 0и затем закройте сокет.
- В любом случае, если возникла какая-либо другая ошибка (например, тайм-аут), просто закройте сокет.
Идеальные реализации для SHUT_RD иSHUT_WR
Следующие не были проверены, доверяйте на свой страх и риск.Тем не менее, я считаю, что это разумный и практичный способ действий.
Если стек TCP получает отключение только с помощью SHUT_RD, он должен пометить это соединение как ожидание данных.Любые ожидающие и последующие read
запросы (независимо от того, в каком потоке они находятся) будут возвращены с нулевым результатом.Тем не менее, соединение все еще активно и доступно - вы все еще можете получать данные OOB, например.Кроме того, ОС будет отбрасывать любые данные, которые она получает для этого подключения.Но это все, никакие пакеты не будут отправлены на другую сторону.
Если стек TCP получает завершение только с SHUT_WR, он должен пометить это соединение, так как больше данных отправлять нельзя.Все ожидающие запросы на запись будут завершены, но последующие запросы на запись не будут выполнены.Кроме того, пакет FIN будет отправлен другой стороне, чтобы сообщить им, что у нас нет больше данных для отправки.