Pregunta

¿Es posible deseleccionar en un socket después de que haya llamado a listen (fd, backlog)?

Edit: Mi error por no aclararme. Me gustaría poder deshabilitar temporalmente el socket. Llamar a close () dejará el socket en el estado M2LS y evitará que lo vuelva a abrir (o, lo que es peor, algún programa nefario podría vincularse a ese socket)

La desinstalación temporal sería una forma (quizás no la mejor) de señalar a un equilibrador de carga ascendente que esta aplicación no podría aceptar más solicitudes por el momento

¿Fue útil?

Solución

Algunas bibliotecas de sockets le permiten rechazar específicamente las conexiones entrantes. Por ejemplo: GNU CommonC ++: TCPsocket Class tiene un rechazar método.

BSD Sockets no tiene esta funcionalidad. Puede aceptar la conexión e inmediatamente cerrar , mientras deja el socket abierto:

while (running) {

  int i32ConnectFD = accept(i32SocketFD, NULL, NULL);
  while (noConnectionsPlease) {
    shutdown(i32ConnectFD, 2);
    close(i32ConnectFD);
    break;
  }

}

Otros consejos

Después de cerrar el socket, es posible que tus programas aún te digan que el socket está " en uso " ;, esto es debido a algunas rarezas de las que no sé exactamente. Pero la página de manual de sockets le muestra que hay una bandera para reutilizar el mismo socket, llamada perezosamente: " SO_REUSEADDR " Configúrelo con " setsockopt () " ;.

Ciérralo. Como recuerdo;

close(fd);

Según su versión editada de la pregunta, no estoy seguro de que tenga que " anular la lista " o cerrar (). Dos opciones vienen a la mente:

1) Después de invocar listen (), las conexiones no son realmente aceptadas hasta que (lógicamente) llames accept (). Puede " anular la lista " simplemente ignorando la actividad del socket y aplazando los aceptar () hasta que esté listo para ellos. Cualquier conexión entrante intenta acumularse en la cola que se creó cuando el puerto se abrió en modo de escucha. Una vez que la cola de backlog está llena en la pila, los intentos de conexión adicionales simplemente se dejan caer en el piso. Cuando reanude con aceptados (), eliminará rápidamente el registro y estará listo para más conexiones.

2) Si realmente desea que el puerto aparezca completamente cerrado temporalmente, puede aplicar dinámicamente el filtro de paquetes a nivel del kernel al puerto para evitar que los intentos de conexión entrante lleguen a la pila de la red. Por ejemplo, podría usar Berkeley Packet Filter (BPF) en la mayoría de las plataformas * nix. Es decir, desea eliminar los paquetes entrantes que llegan al puerto de interés mediante las funciones de firewall de la plataforma. Esto, por supuesto, varía según la plataforma, pero es un enfoque posible.

No creo que sea una buena manera de señalar un equilibrador de carga ascendente. Tendría que enviar realmente algunas conexiones a su servidor antes de que llegara el mensaje. Es probable que esas conexiones se rechacen.

Del mismo modo, cualquier conexión que estuviera pendiente cuando cerró el zócalo de escucha se cerrará sin datos.

Si desea señalar el equilibrador de carga en sentido ascendente, debe tener un protocolo para hacerlo. No intentes abusar de TCP para hacerlo.

Afortunadamente, si los clientes son navegadores web normales, puedes salirte con una gran cantidad; simplemente cerrando sockets generalmente resulta en que vuelvan a intentarlo de forma transparente para el usuario (hasta cierto punto).

¡No hay ningún método explícito para anular la lista!

Puede cerrar (fd) o shutdown (fd, how)

fd is the socket file descriptor you want to shutdown, and how is one of the following:

0 Further receives are disallowed

1 Further sends are disallowed

2 Further sends and receives are disallowed (like close())

En un nivel básico, los sockets están abiertos o cerrados (ignoraremos las sutilezas del diagrama de estado de TCP / IP aquí).

Si su socket está cerrado, entonces nada puede enviarle datos. Si está abierto, los datos entrantes serán aceptados y confirmados por la pila TCP / IP hasta que su algoritmo de búfer grite "¡ya es suficiente!". En ese punto, no se reconocerán más datos.

Tienes dos opciones que puedo ver. Cierre () el socket cuando desee " unlisten " ;, y vuelva a abrirlo más tarde: use setsockopt () con el indicador SO_REUSEADDR para permitirle volver a vincular al puerto conocido antes de que expire TIME_WAIT2.

La otra opción es mantener el zócalo abierto, pero simplemente no aceptar () cuando esté 'ocupado'. Suponiendo que tenga un acuse de recibo a nivel de las solicitudes, el equilibrador de carga se dará cuenta de que no está recibiendo una respuesta y actuará en consecuencia.

Este es un enfoque bastante feo basado en tu pregunta editada:

Abra un zócalo para escuchar con un retraso normal. Continuar.

Cuando quieras " cerrar " ;, abre un segundo con un registro de 1 y SO_REUSEADDR. Cierra el primero. Cuando esté listo para reanudar, haga otro malabarismo de zócalo a uno con un atraso normal.

Los detalles delicados sobre el drenaje de la cola de aceptación del socket que está cerrando serán el asesino aquí. Probablemente sea un asesino suficiente para hacer que este enfoque sea inviable.

No necesariamente creo que esta sea una buena idea, pero ...

Es posible que puedas escuchar por segunda vez. La especificación POSIX no dice que no. Tal vez podría llamarlo por segunda vez con un parámetro de reserva de 0 cuando desee " anular la lista " ;.

Lo que sucede cuando se llama a listen con un backlog de 0 parece estar definido por la implementación. La especificación POSIX dice que puede permitir que se acepten conexiones, lo que implica que algunas implementaciones pueden optar por rechazar todas las conexiones si el parámetro de acumulación es 0. Sin embargo, es más probable que su implementación elija algún valor positivo cuando pase en 0 (probablemente 1 o SOMAXCONN).

La pregunta no dice qué tipo de socket. Si es un socket Unix, puede detener y comenzar a escuchar con renombrar (2). También puede dejar de escuchar permanentemente con unlink (2), y como el socket permanece abierto, puede continuar atendiendo su trabajo pendiente. Este enfoque parece bastante práctico, aunque no lo he visto usado antes y solo lo estoy explorando.

Ya tienes algunas respuestas sobre la imposibilidad de hacer esto a través de la API de socket.

Puede usar otros métodos de SO (es decir, Host firwewall / iptables / ipfilter) para configurar una regla de rechazo temporal.

Descubrí que la mayoría de los equilibradores de carga están un poco limitados en las posibilidades que ofrecen para reconocer problemas de conexión (la mayoría de ellos reconoce un RST solo en la sonda de conexión, no como respuesta a un intento de conexión legítimo).

De todos modos, si está limitado por las sondas que detectan la falta de disponibilidad, configure una sonda de nivel de aplicación que realice una solicitud HTTP o un inicio de sesión FTP o cosas similares que reconocerá si simplemente cierra después de aceptar. Incluso podría interpretar mensajes de error como "servicio 500 no disponible", lo que me parece más limpio de todos modos. Con SNMP, algunos equilibradores de carga también pueden usar el resultado como una sugerencia de carga.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top