it-swarm.it

zoccolo close vs shutdown?

In C, ho capito che se chiudiamo un socket, significa che il socket sarà distrutto e può essere riutilizzato in seguito.

Che ne dici di un arresto? La descrizione dice che chiude la metà di una connessione duplex a quella presa. Ma quel socket sarà distrutto come la chiamata di sistema close

181
hdn

Questo è spiegato nella guida di networking di Beej. shutdown è un modo flessibile per bloccare la comunicazione in una o entrambe le direzioni. Quando il secondo parametro è SHUT_RDWR, bloccherà sia l'invio che la ricezione (come close). Tuttavia, close è il modo per distruggere effettivamente un socket.

Con shutdown, sarai ancora in grado di ricevere i dati in sospeso che il peer ha già inviato (grazie a Joey Adams per averlo notato).

163
Matthew Flaschen

Nessuna delle risposte esistenti dice alla gente come shutdown e close funzionano a livello di protocollo TCP, quindi vale la pena di aggiungere questo.

Una connessione standard TCP viene terminata tramite la finalizzazione a 4 vie:

  1. Una volta che un partecipante non ha più dati da inviare, invia un pacchetto FIN all'altro
  2. L'altra parte restituisce un ACK per la FIN.
  3. Quando anche l'altra parte ha terminato il trasferimento dei dati, invia un altro pacchetto FIN
  4. Il partecipante iniziale restituisce un ACK e finalizza il trasferimento.

Tuttavia, esiste un altro modo "emergente" per chiudere una connessione TCP:

  1. Un partecipante invia un pacchetto RST e abbandona la connessione
  2. L'altro lato riceve un RST e quindi abbandona anche la connessione

Nel mio test con Wireshark, con le opzioni di socket predefinite, shutdown invia un pacchetto FIN all'altro capo ma è tutto ciò che fa. Fino a quando l'altra parte non ti invierà il pacchetto FIN, potrai comunque ricevere i dati. Una volta che questo è accaduto, il tuo Receive otterrà un risultato di dimensione 0. Quindi se sei il primo a chiudere "invia", dovresti chiudere il socket una volta che hai finito di ricevere i dati.

D'altra parte, se si chiama close mentre la connessione è ancora attiva (l'altro lato è ancora attivo e si possono avere dati non inviati anche nel buffer di sistema), un pacchetto RST verrà inviato all'altro lato. Questo è buono per gli errori. Ad esempio, se si ritiene che l'altra parte abbia fornito dati errati o abbia rifiutato di fornire dati (attacco DOS?), È possibile chiudere immediatamente la presa.

La mia opinione sulle regole sarebbe:

  1. Quando possibile, considera shutdown before close
  2. Se hai finito di ricevere (dati di dimensione 0 ricevuti) prima di decidere di spegnerlo, chiudi la connessione dopo l'ultima mandata (se presente) terminata.
  3. Se vuoi chiudere la connessione normalmente, chiudi la connessione (con SHUT_WR e se non ti interessa ricevere dati dopo questo punto, anche con SHUT_RD), e attendi fino a quando non ricevi un dato di dimensione 0, e poi chiudi zoccolo.
  4. In ogni caso, se si verifica un altro errore (timeout ad esempio), basta chiudere il socket.

Implementazioni ideali per SHUT_RD e SHUT_WR

Quanto segue non è stato testato, affidatevi a vostro rischio. Tuttavia, credo che questo sia un modo ragionevole e pratico di fare le cose.

Se lo stack TCP riceve solo un arresto con SHUT_RD, contrassegna questa connessione in modo che non ci siano più dati previsti. Qualsiasi richiesta in sospeso e successive read (indipendentemente dal thread in cui si trovano) verrà quindi restituita con un risultato di dimensioni zero. Tuttavia, la connessione è ancora attiva e utilizzabile: è ancora possibile ricevere dati OOB, ad esempio. Inoltre, il sistema operativo cancellerà tutti i dati ricevuti per questa connessione. Ma questo è tutto, nessun pacco sarà spedito dall'altra parte.

Se lo stack TCP riceve un arresto solo con SHUT_WR, contrassegna questa connessione poiché non è possibile inviare altri dati. Tutte le richieste di scrittura in sospeso saranno terminate, ma le richieste di scrittura successive non riusciranno. Inoltre, un pacchetto FIN verrà inviato ad un altro lato per informarli che non abbiamo più dati da inviare.

110
Earth Engine

Esistono alcune limitazioni con close() che possono essere evitate se si utilizza shutdown().

close() terminerà entrambe le direzioni su una connessione TCP. A volte vuoi dire all'altro endpoint che hai finito di inviare dati, ma vuoi comunque ricevere i dati.

close() decrementa il conteggio dei riferimenti dei descrittori (mantenuto nella voce della tabella file e conteggia il numero di descrittori attualmente aperti che si riferiscono a un file/socket) e non chiude il socket/file se il descrittore non è 0. Ciò significa che se si esegue la foratura , la pulizia avviene solo dopo che il conteggio dei riferimenti scende a 0. Con shutdown() si può iniziare la normale sequenza di chiusura TCP ignorando il conteggio dei riferimenti.

I parametri sono i seguenti:

int shutdown(int s, int how); // s is socket descriptor

int how può essere:

SHUT_RD o 0 Ulteriori ricevimenti non sono consentiti

SHUT_WR o 1 Ulteriori mandati non sono consentiti

SHUT_RDWR o 2 Ulteriori mosse e ricevute non sono consentite

33
Milan

Questo potrebbe essere specifico per la piattaforma, in qualche modo ne dubito, ma in ogni caso, la migliore spiegazione che ho visto è in questa pagina di msdn dove spiegano le opzioni di spegnimento, di pausa, la chiusura della presa e le sequenze di terminazione della connessione generale.

In breve, usa shutdown per inviare una sequenza di spegnimento al livello TCP e usa vicino per liberare le risorse utilizzate dalle strutture di dati del socket nel tuo processo. Se non hai emesso una sequenza di spegnimento esplicita per il momento in cui chiami chiudi, ne viene avviato uno per te.

15
Len Holgate

"shutdown () in realtà non chiude il descrittore di file, cambia solo la sua usabilità. Per liberare un descrittore di socket, devi usare close ()." 1

Ho anche avuto successo sotto linux usando shutdown() da un pthread per forzare un altro pthread attualmente bloccato in connect() ad abortire presto. 

Sotto altri sistemi operativi (almeno OSX), ho trovato che chiamare close() era sufficiente per far fallire connect().

7
Toby

Vicino

Quando hai finito di usare un socket, puoi semplicemente chiudere il suo descrittore di file con close; Se ci sono ancora dati in attesa di essere trasmessi tramite la connessione, normalmente chiudi tenta di completare questa trasmissione. Puoi controllare questo comportamento usando l'opzione socket SO_LINGER per specificare un periodo di timeout; vedi Opzioni socket. 

Spegnimento

È inoltre possibile arrestare solo la ricezione o la trasmissione su una connessione chiamando shutdown.

La funzione di spegnimento interrompe la connessione della presa. Il suo argomento come specifica quale azione eseguire: 0 Smettere di ricevere dati per questo socket. Se arrivano altri dati, rifiutalo. 1 Smetti di provare a trasmettere dati da questo socket. Elimina tutti i dati in attesa di essere inviati. Smetti di cercare il riconoscimento dei dati già inviati; non ritrasmetterlo se è perso. 2 Interrompe sia la ricezione che la trasmissione. 

Il valore restituito è 0 in caso di successo e -1 in caso di fallimento.

3
Neha Agrawal

nel mio test.

close invierà finet pacchetto e distruggerà fd immediatamente quando il socket non è condiviso con altri processi

shutdown SHUT_RD , il processo può ancora recuperare dati dal socket, ma recv restituirà 0 se il buffer TCP è vuoto. Dopo che peer invia più dati, recv restituirà nuovamente i dati.

shutdown SHUT_WR invierà il pacchetto di fin per indicare che le ulteriori mandate non sono consentite. il peer può recv data ma recv 0 se il suo buffer TCP è vuoto

shutdown SHUT_RDWR (uguale a usare entrambi SHUT_RD e SHUT_WR ) invierà il primo pacchetto se peer invierà più dati.

1
simpx