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 quella presa sarà distrutta come close chiamata di sistema close ?

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).

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

Una connessione TCP standard viene terminata con 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 ti invia il pacchetto FIN, sei ancora in grado di ricevere dati. Una volta che questo è successo, 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 triggers (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 è un bene per gli errori. Ad esempio, se si ritiene che l’altra parte abbia fornito dati errati o si sia rifiutato di fornire dati (attacco DOS?), È ansible chiudere immediatamente la presa.

La mia opinione sulle regole sarebbe:

  1. Considerare l’ shutdown prima di close quando ansible
  2. Se hai finito di ricevere (dati di dimensione 0 ricevuti) prima di decidere di spegnere, 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, quindi 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

I seguenti non sono stati testati, affidati a tuo rischio. Tuttavia, credo che questo sia un modo ragionevole e pratico di fare le cose.

Se lo stack TCP riceve un arresto solo con SHUT_RD, deve contrassegnare questa connessione come non ci sono più dati attesi. Qualsiasi richiesta di read sospeso e successiva (indipendentemente dal thread in cui si trovano) verrà quindi restituita con un risultato di dimensioni zero. Tuttavia, la connessione è ancora triggers e utilizzabile: è ancora ansible 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, deve contrassegnare questa connessione in quanto non è ansible 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.

Esistono alcune limitazioni con close() che può essere evitato 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 conta 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 stanno biforcandosi, 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 altri SHUT_RD non sono consentiti

SHUT_WR o 1 ulteriori mandi non sono consentiti

SHUT_RDWR o 2 Ulteriori mandi e ricevi non sono consentiti

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

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

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

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

“shutdown () in realtà non chiude il descrittore del file, cambia solo la sua usabilità.Per liberare un descrittore di socket, è necessario utilizzare close ().” 1

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 ansible arrestare solo la ricezione o la trasmissione su una connessione chiamando shutdown.

La funzione di spegnimento interrompe la connessione della presa. La sua argomentazione su come specificare quale azione eseguire: 0 Interrompere la ricezione di dati per questo socket. Se arrivano altri dati, rifiutalo. 1 Interrompere il tentativo di 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.

nel mio test

close invierà il pacchetto di fin 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 aver inviato più dati, recv restituirà di nuovo i dati.

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

shutdown SHUT_RDWR (uguale a utilizzare sia SHUT_RD che SHUT_WR ) invierà il primo pacchetto se peer invierà più dati.