Nessuna delle risposte esistenti dice alla gente come shutdown
eclose
funziona a livello di protocollo TCP, quindi vale la pena aggiungere questo.
Una connessione TCP standard viene terminata dalla finalizzazione a 4 vie:
- Una volta che un partecipante non ha più dati da inviare, invia un pacchetto FIN all'altro
- L'altra parte restituisce un ACK per la FIN.
- Quando anche l'altra parte ha terminato il trasferimento dei dati, invia un altro pacchetto FIN
- Il partecipante iniziale restituisce un ACK e finalizza il trasferimento.
Tuttavia, esiste un altro modo "emergente" per chiudere una connessione TCP:
- Un partecipante invia un pacchetto RST e abbandona la connessione
- L'altra parte riceve un RST e quindi abbandona anche la connessione
Nel mio test con Wireshark, con le opzioni socket predefinite, shutdown
invia un pacchetto FIN all'altra estremità, ma è tutto ciò che fa. Fino a quando l'altra parte non ti invia il pacchetto FIN, è ancora possibile ricevere i dati. Una volta che questo è successo, Receive
otterrai un risultato di dimensione 0. Quindi, se sei il primo a chiudere "invia", dovresti chiudere il socket una volta terminata la ricezione dei dati.
D'altra parte, se chiami close
mentre la connessione è ancora attiva (l'altro lato è ancora attivo e si potrebbero avere anche dati non inviati 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 che abbia rifiutato di fornire dati (attacco DOS?), È possibile chiudere immediatamente il socket.
La mia opinione sulle regole sarebbe:
- Considerare
shutdown
prima close
quando possibile
- Se hai finito di ricevere (dati di dimensioni 0 ricevuti) prima di decidere di chiudere, chiudi la connessione dopo l'ultimo invio (se presente).
- Se vuoi chiudere normalmente la connessione, 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 il zoccolo.
- In ogni caso, se si sono verificati altri errori (timeout, ad esempio), è sufficiente chiudere il socket.
Implementazioni ideali per SHUT_RD e SHUT_WR
Quanto segue non è stato testato, affidati a tuo rischio e pericolo. 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, contrassegnerà questa connessione come nessun altro dato previsto. Eventuali read
richieste in sospeso e successive (indipendentemente dal thread in cui si trovano) verranno quindi restituite con risultato di dimensioni zero. Tuttavia, la connessione è ancora attiva e utilizzabile, ad esempio è ancora possibile ricevere dati OOB. Inoltre, il sistema operativo eliminerà tutti i dati ricevuti per questa connessione. Ma questo è tutto, nessun pacchetto verrà inviato dall'altra parte.
Se lo stack TCP riceve un arresto solo con SHUT_WR, deve contrassegnare questa connessione in quanto non è possibile inviare altri dati. Tutte le richieste di scrittura in sospeso saranno terminate, ma le successive richieste di scrittura falliranno. Inoltre, un pacchetto FIN verrà inviato a un'altra parte per informarli che non abbiamo più dati da inviare.