Creo que entiendo el significado formal de la opción. En algún código heredado que estoy manejando ahora, se usa la opción. El cliente se queja de RST como respuesta a FIN desde su lado al cerrar la conexión desde su lado.
No estoy seguro de poder quitarlo de manera segura, ya que no entiendo cuándo debe usarse.
¿Puede dar un ejemplo de cuándo sería necesaria la opción?
Respuestas:
La razón típica para establecer un
SO_LINGER
tiempo de espera de cero es evitar un gran número de conexiones en elTIME_WAIT
estado, lo que inmoviliza todos los recursos disponibles en un servidor.Cuando una conexión TCP se cierra limpiamente, el extremo que inició el cierre ("cierre activo") termina con la conexión
TIME_WAIT
durante varios minutos. Entonces, si su protocolo es uno en el que el servidor inicia el cierre de la conexión e involucra una gran cantidad de conexiones de corta duración, entonces podría ser susceptible a este problema.Sin embargo, esta no es una buena idea,
TIME_WAIT
existe por una razón (para garantizar que los paquetes perdidos de conexiones antiguas no interfieran con las conexiones nuevas). Es una mejor idea rediseñar su protocolo a uno en el que el cliente inicie la conexión, si es posible.fuente
TIME_WAIT
se sentará al lado del cliente sin hacerle daño. Recuerde como dice en "Programación de red UNIX" tercera edición (Stevens et al) página 203: "El estado TIME_WAIT es su amigo y está ahí para ayudarnos. En lugar de tratar de evitar el estado, debemos entenderlo (Sección 2.7) . "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:
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
close()
FIN
a BFIN_WAIT_1
estadoFIN
ACK
a ACLOSE_WAIT
estadoACK
FIN_WAIT_2
estadoclose()
FIN
a ALAST_ACK
estadoFIN
ACK
a BTIME_WAIT
estadoACK
CLOSED
estado, es decir, se elimina de las tablas de conectoresTIEMPO DE ESPERA
Por lo tanto, el par que inicia la terminación, es decir, llama
close()
primero, terminará en elTIME_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 llamadaclose()
hará que no se inicie la secuencia de terminación normal .En cambio, el par que configura esta opción y llama
close()
enviará unRST
(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 llamarclose()
; de ahora en adelante, se llama cierre abortivo. , en una aplicación de servidor.Sin embargo, cierta situación justifica hacerlo de todos modos:
CLOSE_WAIT
o terminar en elTIME_WAIT
estado.TIME_WAIT
(al llamarclose()
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.CLOSE_WAIT
intentar entregar datos a un terminal atascado puerto, pero restablecería correctamente el puerto atascado si tuvieraRST
que descartar los datos pendientes ".Recomendaría este extenso artículo que creo que da una muy buena respuesta a tu pregunta.
fuente
TIME_WAIT
es un amigo solo cuando no comienza a causar problemas: stackoverflow.com/questions/1803566/…Cuando el tiempo de espera está activado pero el tiempo de espera es cero, la pila TCP no espera a que se envíen los datos pendientes antes de cerrar la conexión. Los datos podrían perderse debido a esto, pero al configurar persistir de esta manera, lo acepta y solicita que la conexión se restablezca de inmediato en lugar de cerrarla correctamente. Esto hace que se envíe un RST en lugar del FIN habitual.
Gracias a EJP por su comentario, consulte aquí para obtener más detalles.
fuente
Si puede eliminar la persistencia en su código de forma segura o no depende del tipo de su aplicación: ¿es un "cliente" (abriendo conexiones TCP y cerrándolas activamente primero) o es un "servidor" (escuchando un TCP abierto y cerrarlo después de que el otro lado inició el cierre)?
Si su aplicación tiene el sabor de un "cliente" (cerrando primero) Y usted inicia y cierra una gran cantidad de conexiones a diferentes servidores (por ejemplo, cuando su aplicación es una aplicación de monitoreo que supervisa la accesibilidad de una gran cantidad de servidores diferentes) su aplicación tiene el problema de que todas las conexiones de sus clientes están bloqueadas en el estado TIME_WAIT. Luego, recomendaría acortar el tiempo de espera a un valor más pequeño que el predeterminado para seguir apagando correctamente pero liberar los recursos de conexiones del cliente antes. No establecería el tiempo de espera en 0, ya que 0 no se apaga correctamente con FIN pero aborta con RST.
Si su aplicación tiene el sabor de un "cliente" y tiene que buscar una gran cantidad de archivos pequeños del mismo servidor, no debe iniciar una nueva conexión TCP por archivo y terminar en una gran cantidad de conexiones de cliente en TIME_WAIT, pero mantenga la conexión abierta y obtenga todos los datos a través de la misma conexión. La opción de persistencia puede y debe eliminarse.
Si su aplicación es un "servidor" (cierre en segundo lugar como reacción al cierre de un par), al cerrar () su conexión se cierra con gracia y los recursos se liberan ya que no ingresa al estado TIME_WAIT. No se debe utilizar Linger. Pero si su aplicación de servidor tiene un proceso de supervisión que detecta conexiones abiertas inactivas inactivas durante un tiempo prolongado (debe definirse "largo"), puede cerrar esta conexión inactiva por su parte, verla como una especie de manejo de errores, con un cierre abortivo. Esto se hace estableciendo el tiempo de espera persistente en 0. close () luego enviará un RST al cliente, diciéndole que está enojado :-)
fuente
En los servidores, es posible que desee enviar en
RST
lugar deFIN
desconectar a los clientes que se comportan mal. Eso omiteFIN-WAIT
seguido deTIME-WAIT
estados de socket en el servidor, lo que evita que se agoten los recursos del servidor y, por lo tanto, protege de este tipo de ataque de denegación de servicio.fuente
Me gusta la observación de Maxim de que los ataques de DOS pueden agotar los recursos del servidor. También sucede sin un adversario realmente malicioso.
Algunos servidores tienen que lidiar con el 'ataque DOS involuntario' que ocurre cuando la aplicación cliente tiene un error de fuga de conexión, donde siguen creando una nueva conexión para cada nuevo comando que envían a su servidor. Y luego tal vez eventualmente cerrando sus conexiones si golpean la presión del GC, o tal vez las conexiones eventualmente se agoten.
Otro escenario es cuando "todos los clientes tienen la misma dirección TCP". Entonces, las conexiones del cliente se distinguen solo por los números de puerto (si se conectan a un solo servidor). Y si los clientes comienzan a ciclar rápidamente las conexiones de apertura / cierre por cualquier motivo, pueden agotar el espacio de tupla (dirección de cliente + puerto, IP de servidor + puerto).
Por lo tanto, creo que es mejor recomendar a los servidores que cambien a la estrategia Linger-Zero cuando vean una gran cantidad de sockets en el estado TIME_WAIT; aunque no corrige el comportamiento del cliente, podría reducir el impacto.
fuente
El socket de escucha en un servidor puede usar el tiempo de espera con 0 para tener acceso al enlace de regreso al socket inmediatamente y para restablecer cualquier cliente cuyas conexiones aún no hayan terminado de conectarse. TIME_WAIT es algo que solo es interesante cuando tiene una red de múltiples rutas y puede terminar con paquetes mal ordenados o, de lo contrario, está lidiando con un pedido / tiempo de llegada extraño de paquetes de red.
fuente