socket.shutdown против socket.close

122

Недавно я видел фрагмент кода, который выглядел так (конечно, sock был объектом сокета):

sock.shutdown(socket.SHUT_RDWR)
sock.close()

Какова именно цель вызова выключения сокета, а затем его закрытия? Если это имеет значение, этот сокет используется для неблокирующего ввода-вывода.

Джейсон Бейкер
источник

Ответы:

38

Вот одно объяснение :

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

Боб Надлер
источник
241

Вызов closeи shutdownимеют два разных эффекта на базовый сокет.

Первое, на что следует обратить внимание, это то, что сокет является ресурсом в базовой ОС и несколько процессов могут иметь дескриптор для одного и того же базового сокета.

Когда вы вызываете closeего, счетчик дескрипторов уменьшается на единицу, и если счетчик дескрипторов достиг нуля, то сокет и связанное с ним соединение проходят обычную процедуру закрытия (фактически отправляя FIN / EOF партнеру), и сокет освобождается.

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

С другой стороны, вызов shutdownдля чтения и записи закрывает базовое соединение и отправляет FIN / EOF партнеру, независимо от того, сколько процессов имеет дескрипторы для сокета. Однако он не освобождает сокет, и вам все равно нужно вызвать close после этого.

Роберт С. Барнс
источник
отличный ответ, я никогда не удосужился выяснить, что shutdown()делает :)
Мэтт Джойнер,
2
Может ли shutdown () для чтения вызвать отправку пакета FIN? т.е. выключение (sock_fd, 1);
Эрнесто,
Было бы разумно всегда звонить .shutdown()и на следующей линии .close()? Или должна быть задержка между ними?
Люк
@Luc Полностью зависит от того, что вы делаете.
Роберт С. Барнс
2
@Luc Тогда просто закрыть - это нормально, если у других процессов нет дескриптора сокета.
Роберт С. Барнс
17

Объяснение выключения и закрытия: плавное выключение (msdn)

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

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

ИМО названия «выключение» и «закрытие» вводят в заблуждение, «закрыть» и «уничтожить» подчеркнут их различия.

Дейл Рейди
источник
Это не указывает на другой конец, что нет намерения читать дальше. Завершение работы для чтения ничего не отправляет партнеру.
Marquis of Lorne
7

он упоминается прямо в HOWTO по программированию сокетов ( py2 / py3 )

Отсоединение

Строго говоря, вы должны использовать shutdownсокет до того, как вы closeэто сделаете . Это shutdownсовет к сокету на другом конце. В зависимости от аргумента, который вы передаете, это может означать « Я больше не буду отправлять, но я все равно буду слушать » или « Я не слушаю, скатертью дорога! ». Однако большинство библиотек сокетов так используются программистами, которые пренебрегают этим правилом этикета, что обычно a closeсовпадает с shutdown(); close(). Поэтому в большинстве ситуаций явное отключение не требуется.

...

mykhal
источник
Эта информация неверна. Когда-либо необходимо выключать сокет для записи, только если (1) вы разветвили процесс и определенно хотите отправить FIN сейчас, или (2) вы участвуете во взаимном протоколе чтения в EOS, так что оба одноранговых узла закрываются в то же время. В противном случае close()достаточно. Документацию Python следует исправить.
Marquis of Lorne
4

Не ошибается ли приведенный выше код?

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

Согласно http://blog.netherlabs.nl/articles/2009/01/18/the-ultimate-so_linger-page-or-why-is-my-tcp-not-reliable нужно подождать между выключением и закрыть, пока чтение не вернет 0.

Кристиан
источник
Не верно. Ядро будет отбрасывать исходящие буферы только в том случае, если соединение было сброшено, что может произойти, если локальное приложение не прочитало все ожидающие входящие данные, которые уже прибыли, или если одноранговый узел испортил SO_LINGER, чего они не должны делать , Нет необходимости ни спать, ни даже вызывать выключение перед закрытием. По этому поводу существует много дезинформации.
Marquis of Lorne
3

есть несколько вариантов завершения работы: http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.shutdown.aspx . * nix аналогично.

Рэй Тайек
источник
Есть одна «разновидность» выключения и два варианта, которые можно комбинировать. Ничто здесь не отвечает на актуальный вопрос.
Marquis of Lorne
1

Shutdown (1), заставляет сокет не отправлять больше данных

Это полезно в

1- Промывка буфера

2- Обнаружение странной ошибки

3- Надежная охрана

Позвольте мне объяснить больше, когда вы отправляете данные из A в B, они не гарантируются, что они будут отправлены в B, они гарантированно будут отправлены только в буфер A os, который, в свою очередь, отправляет их в буфер B os.

Таким образом, вызывая shutdown (1) для A, вы очищаете буфер A, и возникает ошибка, если буфер не пуст, то есть: данные еще не отправлены партнеру

Как бы то ни было, это необратимо, поэтому вы можете сделать это после того, как полностью отправите все свои данные, и вы хотите быть уверены, что они по крайней мере в буфере одноранговой ОС.

user306166
источник
Завершение работы не приводит к принудительной промывке. Ситуация с буферизацией не изменилась. И если буфер не пуст, ошибка не возникает. FIN просто ставится в очередь после ожидающих данных. Ответ совершенно неверный.
Marquis of Lorne