¿Cuándo se requiere la opción TCP SO_LINGER (0)?

Resuelto dimba asked hace 13 años • 8 respuestas

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 sobre la conexión cerrada desde su lado.

No estoy seguro de poder eliminarlo de forma segura, ya que no entiendo cuándo se debe utilizar.

¿Puede darnos un ejemplo de cuándo sería necesaria la opción?

dimba avatar Sep 21 '10 11:09 dimba
Aceptado

Para mi sugerencia, lea la última sección: "Cuándo usar SO_LINGER con tiempo de espera 0" .

Antes de llegar a eso, una pequeña conferencia sobre:

  • Terminación TCP normal
  • TIME_WAIT
  • FIN, ACKyRST

Terminación TCP normal

La secuencia de terminación TCP normal se ve así (simplificada):

Tenemos dos pares: A y B.

  1. una llamadaclose()
  • A envía FINa B
  • A entra en FIN_WAIT_1estado
  1. B recibeFIN
  • B envía ACKa A
  • B entra en CLOSE_WAITestado
  1. un recibeACK
  • A entra en FIN_WAIT_2estado
  1. B llamaclose()
  • B envía FINa A
  • B entra en LAST_ACKestado
  1. un recibeFIN
  • A envía ACKa B
  • A entra en TIME_WAITestado
  1. B recibeACK
  • B pasa al CLOSEDestado, es decir, se elimina de las tablas de sockets.

TIEMPO DE ESPERA

Por lo tanto, el par que inicia la terminación (es decir, que llama close()primero) terminará en ese TIME_WAITestado.

Para comprender por qué el TIME_WAITestado 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 sockets en TIME_WAITestado en un servidor, ya que eventualmente podría impedir que se acepten nuevas conexiones.

Para solucionar este problema, he visto a muchos sugerir configurar la opción de socket SO_LINGER con 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 el protocolo de su aplicación de modo 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-Lengthencabezado HTTP cuándo ha leído todos los datos y puede iniciar el cierre. (Sé que en HTTP 1.1 lo mantendrá abierto por 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

Nuevamente, de acuerdo con la tercera edición de "Programación de red UNIX", páginas 202-203, la configuración SO_LINGERcon tiempo de espera 0 antes de la llamada close()provocará 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. Normalmente verá errores como "Conexión restablecida por igual".

Por lo tanto, en una situación normal es realmente una mala idea establecer SO_LINGERun tiempo de espera de 0 antes de llamar close()(de ahora en adelante llamado cierre abortivo ) en una aplicación de servidor.

Sin embargo, ciertas situaciones justifican hacerlo de todos modos:

  • Si un cliente de su aplicación de servidor se porta mal (se agota el tiempo de espera, devuelve datos no válidos, etc.), tiene sentido realizar un cierre abortivo para evitar quedarse atrapado CLOSE_WAITo terminar en ese TIME_WAITestado.
  • Si debe reiniciar la aplicación de su servidor, que actualmente tiene miles de conexiones de clientes, podría 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 impedir que el servidor obtenga puertos disponibles para nuevas conexiones de clientes. después de ser reiniciado.
  • En la página 202 del libro antes mencionado dice específicamente: "Existen 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 podría bloquearse para siempre al CLOSE_WAITintentar entregar datos a un terminal atascado. puerto, pero restablecería correctamente el puerto atascado si consiguiera RSTdescartar los datos pendientes".

Recomendaría este extenso artículo que creo que da una muy buena respuesta a su pregunta.

mgd avatar Oct 26 '2012 14:10 mgd

La razón típica para establecer un SO_LINGERtiempo de espera de cero es evitar una gran cantidad de conexiones en el TIME_WAITestado, ocupando 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 inactiva TIME_WAITdurante varios minutos. Entonces, si su protocolo es uno en el que el servidor inicia la conexión e involucra una gran cantidad de conexiones de corta duración, entonces podría ser susceptible a este problema.

Sin embargo, esto no es una buena idea: TIME_WAITexiste por una razón (para garantizar que los paquetes perdidos de conexiones antiguas no interfieran con las nuevas). Es una mejor idea rediseñar su protocolo a uno en el que el cliente inicie la conexión, si es posible.

caf avatar Sep 21 '2010 12:09 caf

Cuando la persistencia está activada 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. Se podrían perder datos debido a esto, pero al configurar la permanencia de esta manera, lo acepta y solicita que la conexión se restablezca de inmediato en lugar de cerrarse 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.

Len Holgate avatar Sep 21 '2010 07:09 Len Holgate

El hecho de que pueda eliminar los restos de su código de forma segura o no depende del tipo de su aplicación: ¿es un "cliente" (que abre conexiones TCP y las cierra activamente primero) o es un "servidor" (que escucha un TCP abierto y cerrarlo después de que la otra parte inició el cierre)?

Si su aplicación tiene el tipo de "cliente" (se cierra 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 menor que el predeterminado para seguir apagándose correctamente pero liberar los recursos de conexiones del cliente antes. No establecería el tiempo de espera en 0, ya que 0 no se cierra correctamente con FIN pero aborta con RST.

Si su aplicación tiene el tipo de "cliente" y tiene que recuperar 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, sino mantenga la conexión abierta y obtenga todos los datos a través de la misma conexión. La opción persistente puede y debe eliminarse.

Si su aplicación es un "servidor" (segundo cercano como reacción al cierre del par), al cerrar() su conexión se cierra correctamente 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 ("largo" debe definirse), puede cerrar esta conexión inactiva desde su lado (considérelo 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ás enojado :-)

Grandswiss avatar Jan 11 '2018 09:01 Grandswiss

Acabo de ver que en el RFC de websockets (RFC 6455) , se indica explícitamente que el servidor debe llamar close()primero al socket TCP (!)

Me quedé asombrado, ya que considero que las respuestas/publicaciones de @mgd en este hilo son de facto, y el RFC claramente va en contra de eso. Pero quizás este sería un caso en el que sería aceptable establecer un tiempo de permanencia en 0.

La conexión TCP subyacente, en la mayoría de los casos normales, DEBE ser cerrada primero por el servidor, de modo que mantenga el estado TIME_WAIT y no el cliente.

Estoy muy interesado en escuchar cualquier opinión o idea sobre esto.

stconnell avatar Jul 14 '2021 22:07 stconnell