socket.shutdown vs socket.close

122

Recientemente vi un poco de código que se veía así (con el calcetín siendo un objeto de socket, por supuesto):

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

¿Cuál es exactamente el propósito de llamar al apagado en el zócalo y luego cerrarlo? Si hace la diferencia, este socket se está utilizando para E / S sin bloqueo.

Jason Baker
fuente

Respuestas:

38

Aquí hay una explicación :

Una vez que ya no se necesita un socket, el programa de llamada puede descartar el socket aplicando una subrutina cercana al descriptor del socket. Si un socket de entrega confiable tiene datos asociados cuando ocurre un cierre, el sistema continúa intentando la transferencia de datos. Sin embargo, si los datos aún no se entregan, el sistema descarta los datos. Si el programa de aplicación no tiene uso para ningún dato pendiente, puede usar la subrutina de apagado en el socket antes de cerrarlo.

Bob Nadler
fuente
241

Llamando closey shutdowntiene dos efectos diferentes en el zócalo subyacente.

Lo primero que hay que señalar es que el socket es un recurso en el sistema operativo subyacente y múltiples procesos pueden tener un controlador para el mismo socket subyacente.

Cuando lo llama close, disminuye el recuento de identificadores en uno y si el recuento de identificadores ha llegado a cero, el socket y la conexión asociada pasan por el procedimiento de cierre normal (enviando un FIN / EOF al par) y el socket se desasigna.

A lo que debemos prestar atención aquí es que si el recuento de identificadores no llega a cero porque otro proceso todavía tiene un identificador para el socket, la conexión no se cierra y el socket no se desasigna.

Por otro lado, llamar shutdownpara leer y escribir cierra la conexión subyacente y envía un FIN / EOF al igual, independientemente de cuántos procesos tengan identificadores en el socket. Sin embargo, no desasigna el zócalo y aún debe llamar al cierre después.

Robert S. Barnes
fuente
gran respuesta, nunca me molesté en averiguar qué shutdown()hace :)
Matt Joiner
2
¿Puede un apagado () para lectura hace que envíe un paquete FIN? es decir, apagado (sock_fd, 1);
ernesto
¿Sería inteligente llamar siempre .shutdown()y en la siguiente línea .close()? ¿O debería haber un retraso en el medio?
Luc
@Luc depende completamente de lo que estás haciendo.
Robert S. Barnes
2
@Luc Entonces solo cerrar está bien siempre que ningún otro proceso tenga un controlador para el socket.
Robert S. Barnes
17

Explicación de apagado y cierre: cierre elegante (msdn)

El apagado (en su caso) indica al otro extremo de la conexión que no hay más intención de leer o escribir en el zócalo. Luego cerrar libera cualquier memoria asociada con el zócalo.

Omitir el apagado puede hacer que el socket permanezca en la pila del sistema operativo hasta que la conexión se haya cerrado correctamente.

En mi opinión, los nombres 'cerrar' y 'cerrar' son engañosos, 'cerrar' y 'destruir' enfatizarían sus diferencias.

Dale Reidy
fuente
No indica al otro extremo que no hay más intención de leer. El apagado por lectura no envía nada al par.
Marqués de Lorne
7

es mencionado justo en el COMO Enchufe de Programación ( AP2 / AP3 )

Desconectando

Estrictamente hablando, se supone que debes usarlo shutdownen un zócalo antes que tú close. El shutdownes un aviso al zócalo en el otro extremo. Dependiendo del argumento que lo pase, puede significar " No voy a enviar más, pero aún así escucharé ", o " No estoy escuchando, ¡adiós! ". Sin embargo, la mayoría de las bibliotecas de sockets están tan acostumbradas a los programadores que descuidan el uso de esta etiqueta que normalmente closees igual a shutdown(); close(). Entonces, en la mayoría de las situaciones, no se necesita un cierre explícito.

...

mykhal
fuente
Esta información no es correcta. Solo es necesario cerrar un socket para escribir si (1) ha bifurcado el proceso y definitivamente desea enviar el FIN ahora, o (2) está participando en un protocolo mutuo de lectura a EOS para que ambos pares cierren al mismo tiempo. De close()lo contrario es suficiente. La documentación de Python debe corregirse.
Marqués de Lorne
4

¿No es incorrecto este código anterior?

La llamada de cierre directamente después de la llamada de cierre podría hacer que el núcleo descarte todos los búferes salientes de todos modos.

De acuerdo con http://blog.netherlabs.nl/articles/2009/01/18/the-ultimate-so_linger-page-or-why-is-my-tcp-not-reliable uno necesita esperar entre el cierre y el cerrar hasta que la lectura devuelva 0.

cristiano
fuente
Incorrecto. El núcleo solo descartará los búferes salientes si la conexión se ha restablecido, lo que puede suceder si la aplicación local no ha leído todos los datos entrantes pendientes que ya han llegado, o si el par se ha metido con SO_LINGER, lo que no deberían hacer. . Tampoco es necesario dormir, ni siquiera llamar al cierre antes de cerrar. Hay mucha información errónea sobre este asunto.
Marqués de Lorne
1

Shutdown (1), obliga al socket no a enviar más datos

Esto es útil en

1- Lavado de búfer

2- detección de error extraño

3- protección segura

Permítanme explicar más, cuando envía datos de A a B, no se garantiza que se envíen a B, solo se garantiza que se envíen al búfer A os, que a su vez lo envía al búfer B os

Entonces, al llamar a shutdown (1) en A, vacía el búfer de A y se genera un error si el búfer no está vacío, es decir: los datos aún no se han enviado al igual

Sin embargo, esto es irrevocable, por lo que puede hacerlo después de haber enviado por completo todos sus datos y desea asegurarse de que al menos estén en el búfer del sistema operativo par

usuario306166
fuente
El apagado no obliga a una descarga. La situación de almacenamiento en búfer no cambia. Y si el búfer no está vacío, no se genera ningún error. El FIN simplemente se coloca detrás de los datos pendientes. La respuesta es completamente incorrecta.
Marqués de Lorne