socket.shutdown vs socket.close


122

Recentemente ho visto un po 'di codice che assomigliava a questo (con il calzino ovviamente come oggetto socket):

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

Qual è esattamente lo scopo di chiamare l'arresto sul socket e quindi chiuderlo? Se fa la differenza, questo socket viene utilizzato per IO non bloccanti.

Risposte:


38

Ecco una spiegazione :

Una volta che un socket non è più necessario, il programma chiamante può scartarlo applicando una subroutine chiusa al descrittore del socket. Se a un socket di consegna affidabile sono associati dati quando si verifica una chiusura, il sistema continua a tentare il trasferimento dei dati. Tuttavia, se i dati non vengono ancora consegnati, il sistema li elimina. Se il programma applicativo non ha alcuna utilità per i dati in sospeso, può utilizzare la subroutine di spegnimento sul socket prima di chiuderlo.


241

Calling closee shutdownhanno due diversi effetti sul socket sottostante.

La prima cosa da sottolineare è che il socket è una risorsa nel sistema operativo sottostante e più processi possono avere un handle per lo stesso socket sottostante.

Quando chiami close , il conteggio degli handle diminuisce di uno e se il conteggio degli handle ha raggiunto zero, il socket e la connessione associata eseguono la normale procedura di chiusura (inviando effettivamente un FIN / EOF al peer) e il socket viene deallocato.

La cosa a cui prestare attenzione qui è che se il conteggio degli handle non raggiunge lo zero perché un altro processo ha ancora un handle per il socket, la connessione non viene chiusa e il socket non viene deallocato.

D'altra parte, la richiesta shutdowndi lettura e scrittura chiude la connessione sottostante e invia un FIN / EOF al peer indipendentemente dal numero di processi che hanno handle per il socket. Tuttavia, non rilascia il socket ed è comunque necessario chiamare close in seguito.


ottima risposta, non mi sono mai preoccupato di scoprire cosa shutdown()fa :)
Matt Joiner

2
Può un shutdown () per la lettura fa sì che invii un pacchetto FIN? cioè arresto (sock_fd, 1);
ernesto

Sarebbe intelligente chiamare sempre .shutdown()e sulla linea successiva .close()? O dovrebbe esserci un ritardo nel mezzo?
Luc

@Luc Dipende completamente da quello che stai facendo.
Robert S. Barnes,

2
@ Luc Quindi basta chiudere va bene finché nessun altro processo ha un handle per il socket.
Robert S. Barnes,

17

Spiegazione di shutdown e close: Graceful shutdown (msdn)

L'arresto (nel tuo caso) indica all'altra estremità della connessione che non c'è più intenzione di leggere o scrivere sul socket. Quindi close libera tutta la memoria associata al socket.

L'omissione dell'arresto può causare il permanere del socket nello stack dei sistemi operativi fino a quando la connessione non è stata chiusa correttamente.

IMO i nomi "chiusura" e "chiusura" sono fuorvianti, "chiusura" e "distruzione" enfatizzerebbero le loro differenze.


Non indica all'altra estremità che non c'è più intenzione di leggere. L'arresto per lettura non invia nulla al peer.
Marchese di Lorne

7

è menzionato proprio nel Socket Programming HOWTO ( py2 / py3 )

Disconnessione

A rigor di termini, dovresti usarlo shutdownsu una presa prima di te close. Il shutdownè un avvertimento al socket all'altra estremità. A seconda dell'argomento che si passa, può significare " Non ho intenzione di inviare più, ma ascolterò comunque ", oppure " Non sto ascoltando, buon viaggio! ”. La maggior parte delle librerie di socket, tuttavia, sono così abituate ai programmatori che trascurano di usare questo pezzo di etichetta che normalmente a closeè lo stesso di shutdown(); close(). Quindi, nella maggior parte delle situazioni, non è necessario un arresto esplicito.

...


Questa informazione non è corretta. È sempre e solo necessario chiudere un socket per la scrittura se (1) hai biforcato il processo e vuoi assolutamente inviare il FIN ora, o (2) ti stai impegnando in un protocollo di lettura reciproca su EOS in modo che entrambi i peer si chiudano allo stesso tempo. Altrimenti close()è sufficiente. La documentazione di Python dovrebbe essere corretta.
Marchese di Lorne

4

Questo codice sopra non è sbagliato?

La chiamata di chiusura subito dopo la chiamata di arresto potrebbe fare in modo che il kernel scarti comunque tutti i buffer in uscita.

Secondo http://blog.netherlabs.nl/articles/2009/01/18/the-ultimate-so_linger-page-or-why-is-my-tcp-not-reliable è necessario attendere tra lo spegnimento e il chiudi finché read non restituisce 0.


Non corretto. Il kernel scarterà i buffer in uscita solo se la connessione è stata ripristinata, il che può accadere se l'app locale non ha letto tutti i dati in entrata in sospeso che sono già arrivati, o se il peer ha incasinato SO_LINGER, cosa che non dovrebbero fare . Non è nemmeno necessario dormire, né chiamare lo spegnimento prima della chiusura. C'è molta disinformazione in giro su questo argomento.
Marchese di Lorne


1

Shutdown (1), forza il socket no a inviare altri dati

Questo è utile in

1- Lavaggio con tampone

2- Rilevamento di errori strani

3- Protezione sicura

Lascia che ti spieghi di più, quando invii un dato da A a B, non è garantito che venga inviato a B, è garantito solo che venga inviato al buffer A os, che a sua volta lo invia al buffer di B os

Quindi, chiamando shutdown (1) su A, svuoti il ​​buffer di A e viene sollevato un errore se il buffer non è vuoto cioè: i dati non sono stati ancora inviati al peer

Comunque questo è irreversibile, quindi puoi farlo dopo aver inviato completamente tutti i tuoi dati e vuoi essere sicuro che sia almeno nel buffer del sistema operativo peer


L'arresto non forza un lavaggio. La situazione del buffering è invariata. E se il buffer non è vuoto non viene generato alcun errore. La FIN viene semplicemente accodata dietro i dati in sospeso. La risposta è completamente sbagliata.
Marchese di Lorne
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.