Para mi sugerencia, lea la última sección: "Cuándo usar SO_LINGER con timeout 0" .
Antes de llegar a eso, una pequeña conferencia sobre:
- Terminación TCP normal
TIME_WAIT
FIN
, ACK
yRST
Terminación TCP normal
La secuencia de terminación de TCP normal se ve así (simplificada):
Tenemos dos pares: A y B
- A llama
close()
- A envía
FIN
a B
- A entra en
FIN_WAIT_1
estado
- B recibe
FIN
- B envía
ACK
a A
- B entra en
CLOSE_WAIT
estado
- A recibe
ACK
- A entra en
FIN_WAIT_2
estado
- B llama
close()
- B envía
FIN
a A
- B entra en
LAST_ACK
estado
- A recibe
FIN
- A envía
ACK
a B
- A entra en
TIME_WAIT
estado
- B recibe
ACK
- B pasa al
CLOSED
estado, es decir, se elimina de las tablas de conectores
TIEMPO DE ESPERA
Por lo tanto, el par que inicia la terminación, es decir, llama close()
primero, terminará en el TIME_WAIT
estado.
Para comprender por qué el TIME_WAIT
estado es nuestro amigo, lea la sección 2.7 de la tercera edición de "Programación de redes UNIX" de Stevens et al (página 43).
Sin embargo, puede ser un problema con muchos enchufes en TIME_WAIT
estado en un servidor ya que eventualmente podría evitar que se acepten nuevas conexiones.
Para solucionar este problema, he visto muchas sugerencias para configurar la opción de socket SO_LINGER con el tiempo de espera 0 antes de llamar close()
. Sin embargo, esta es una mala solución ya que hace que la conexión TCP finalice con un error.
En su lugar, diseñe su protocolo de aplicación para que la terminación de la conexión siempre se inicie desde el lado del cliente. Si el cliente siempre sabe cuándo ha leído todos los datos restantes, puede iniciar la secuencia de terminación. Por ejemplo, un navegador sabe por el Content-Length
encabezado HTTP cuándo ha leído todos los datos y puede iniciar el cierre. (Sé que en HTTP 1.1 lo mantendrá abierto durante un tiempo para una posible reutilización y luego lo cerrará).
Si el servidor necesita cerrar la conexión, diseñe el protocolo de aplicación para que el servidor le pida al cliente que llame close()
.
Cuándo usar SO_LINGER con tiempo de espera 0
Una vez más, de acuerdo con la tercera edición de "Programación de red UNIX", página 202-203, la configuración SO_LINGER
con tiempo de espera 0 antes de la llamada close()
hará que no se inicie la secuencia de terminación normal .
En cambio, el par que configura esta opción y llama close()
enviará un RST
(restablecimiento de conexión) que indica una condición de error y así es como se percibirá en el otro extremo. Por lo general, verá errores como "Conexión restablecida por par".
Por lo tanto, en la situación normal, es una muy mala idea establecer SO_LINGER
con el tiempo de espera 0 antes de llamar close()
; de ahora en adelante, se llama cierre abortivo. , en una aplicación de servidor.
Sin embargo, cierta situación justifica hacerlo de todos modos:
- Si el cliente de su aplicación de servidor se comporta mal (se agota el tiempo de espera, devuelve datos no válidos, etc.), un cierre abortivo tiene sentido para evitar quedarse atascado
CLOSE_WAIT
o terminar en el TIME_WAIT
estado.
- Si debe reiniciar la aplicación de su servidor, que actualmente tiene miles de conexiones de cliente, puede considerar configurar esta opción de socket para evitar miles de sockets de servidor
TIME_WAIT
(al llamar close()
desde el extremo del servidor), ya que esto podría evitar que el servidor obtenga puertos disponibles para nuevas conexiones de cliente. después de reiniciarse.
- En la página 202 del libro antes mencionado, dice específicamente: "Hay ciertas circunstancias que justifican el uso de esta función para enviar un cierre abortivo. Un ejemplo es un servidor de terminal RS-232, que puede bloquearse para siempre al
CLOSE_WAIT
intentar entregar datos a un terminal atascado puerto, pero restablecería correctamente el puerto atascado si tuviera RST
que descartar los datos pendientes ".
Recomendaría este extenso artículo que creo que da una muy buena respuesta a tu pregunta.