Per il mio suggerimento, leggi l'ultima sezione: "Quando utilizzare SO_LINGER con timeout 0" .
Prima di arrivare a questo, una piccola conferenza su:
- Terminazione TCP normale
TIME_WAIT
FIN, ACKeRST
Terminazione TCP normale
La normale sequenza di terminazione TCP è simile a questa (semplificata):
Abbiamo due pari: A e B
- Una chiamata
close()
- A invia
FINa B
- A entra in
FIN_WAIT_1stato
- B riceve
FIN
- B invia
ACKad A
- B entra nello
CLOSE_WAITstato
- A riceve
ACK
- A entra in
FIN_WAIT_2stato
- B chiama
close()
- B invia
FINad A
- B entra nello
LAST_ACKstato
- A riceve
FIN
- A invia
ACKa B
- A entra in
TIME_WAITstato
- B riceve
ACK
- B passa allo
CLOSEDstato, ovvero viene rimosso dalle tabelle dei socket
TEMPO DI ATTESA
Quindi il peer che avvia la terminazione, ovvero chiama per close()primo, finirà nello TIME_WAITstato.
Per capire perché lo TIME_WAITStato è nostro amico, leggere la sezione 2.7 nella terza edizione "Programmazione di rete UNIX" di Stevens et al (pagina 43).
Tuttavia, può essere un problema con molti socket in TIME_WAIT stato su un server in quanto potrebbe impedire l'accettazione di nuove connessioni.
Per ovviare a questo problema, ho visto molti suggerire di impostare l'opzione socket SO_LINGER con timeout 0 prima di chiamare close(). Tuttavia, questa è una cattiva soluzione poiché causa la chiusura della connessione TCP con un errore.
Progettare invece il protocollo dell'applicazione in modo che la terminazione della connessione venga sempre avviata dal lato client. Se il client sa sempre quando ha letto tutti i dati rimanenti, può avviare la sequenza di terminazione. Ad esempio, un browser sa dall'intestazione Content-LengthHTTP quando ha letto tutti i dati e può avviare la chiusura. (So che in HTTP 1.1 lo manterrà aperto per un po 'per un possibile riutilizzo, quindi lo chiuderà.)
Se il server deve chiudere la connessione, progettare il protocollo dell'applicazione in modo che il server chieda al client di chiamare close().
Quando utilizzare SO_LINGER con timeout 0
Di nuovo, secondo la "Programmazione di rete UNIX", terza edizione, pagina 202-203, l'impostazione SO_LINGERcon timeout 0 prima della chiamata close()farà sì che la normale sequenza di terminazione non venga avviata.
Invece, il peer impostando questa opzione e chiamando close()invierà un RST(ripristino della connessione) che indica una condizione di errore ed è così che verrà percepita dall'altra parte. In genere vedrai errori come "Connessione ripristinata da peer".
Pertanto, nella situazione normale è davvero una cattiva idea impostare SO_LINGERcon timeout 0 prima della chiamata close()- d'ora in poi chiamata chiusura abortiva - in un'applicazione server.
Tuttavia, alcune situazioni lo giustificano comunque:
- Se il client della tua applicazione server si comporta male (va in timeout, restituisce dati non validi, ecc.), Una chiusura abortita ha senso per evitare di rimanere bloccati
CLOSE_WAITo finire nello TIME_WAITstato.
- Se devi riavviare la tua applicazione server che attualmente ha migliaia di connessioni client potresti considerare di impostare questa opzione socket per evitare migliaia di socket server in entrata
TIME_WAIT(quando si chiama close()dal lato server) in quanto ciò potrebbe impedire al server di ottenere le porte disponibili per nuove connessioni client dopo essere stato riavviato.
- A pagina 202 del libro di cui sopra si dice specificamente: "Ci sono alcune circostanze che giustificano l'utilizzo di questa funzione per inviare una chiusura abortita. Un esempio è un terminal server RS-232, che potrebbe bloccarsi per sempre nel
CLOSE_WAITtentativo di fornire dati a un terminale bloccato port, ma reimposterebbe correttamente la porta bloccata se riuscisse RSTa eliminare i dati in sospeso. "
Consiglierei questo lungo articolo che credo dia un'ottima risposta alla tua domanda.