2008-10-02 6 views
11

¿Es posible desconectarse de un socket después de haber llamado listen (fd, backlog)?¿Se puede dejar de enganchar en un enchufe?

Editar: Mi error por no aclararme. Me gustaría poder desconectarme temporalmente del socket. Llamar a close() dejará el socket en el estado M2LS e impedirá que vuelva a abrirlo (o peor, algún programa nefasto podría vincularse a ese socket)

Desvincular temporalmente sería una forma (tal vez no la mejor) de señalar a un equilibrador de carga en sentido ascendente que esta aplicación no podría aceptar más solicitudes por el momento

Respuesta

3

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

BSD Sockets no tiene esta funcionalidad. Puede aceptar la conexión y luego inmediatamente cerca él, dejando el socket abierto:

while (running) { 

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

} 
+0

que no dará el mismo efecto en el nivel de protocolo que no escuchar o rechazar una conexión debido a que el backlog de escucha está lleno, por lo que podría no lograr lo que Dave desea con respecto a balanceadores de carga, etc. Esto se verá como una conexión exitosa de corta duración. –

+0

No es perfecto, pero, hasta donde yo sé, con conectores BSD no hay forma de desconectarse sin perder el zócalo, y no hay forma de enviar un cierre sin obtener primero el asa de socket para la conexión con un accept. Si esto no funciona, entonces Dave necesitará una nueva biblioteca de socket. –

+0

Los enlaces en su respuesta están rotos. –

4

Ciérrelo. Hasta donde recuerdo;

close(fd); 
1

¡No hay un método explícito para desconectar!

Puede close(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()) 
+0

shutdown() puede estar permitido, pero los únicos valores que esperaría tener algún efecto serían 0 o 2, que de todos modos eliminarían toda utilidad en un socket de escucha. close() es más fácil, y más que lo suficientemente explícito, especialmente en (supongo) C. –

5

Después de cerrar el zócalo, los programas pueden todavía le dicen que el zócalo esté "en uso", esto es debido a algún weirdiness No sé exactamente acerca . Pero la página de manual sobre sockets muestra que hay una bandera para volver a usar el mismo socket, llamado de forma perezosa: "SO_REUSEADDR". Configúrelo usando "setsockopt()".

+1

No estoy seguro, pero creo que si desactivas la opción SO_LINGER a través de setsockopt(), puede ser útil. – Ferruccio

0

En un nivel básico, las tomas están abiertas o cerradas (ignoraremos aquí las sutilezas del diagrama de estado de TCP/IP).

Si su socket está cerrado, entonces nada le puede enviar datos. Si está abierto, los datos entrantes serán aceptados y confirmados por la pila TCP/IP hasta que el algoritmo de almacenamiento intermedio grite "¡suficiente!". En ese momento, no se reconocerán más datos.

Tiene dos opciones que puedo ver. Cierre() el socket cuando desee "unlisten" y vuélvalo a abrir más tarde: use setsockopt() con el indicador SO_REUSEADDR para permitirle volver a vincular al puerto conocido antes de que caduque TIME_WAIT2.

La otra opción es mantener el socket abierto pero simplemente no aceptar() mientras está 'ocupado'. Suponiendo que tiene un reconocimiento de nivel de aplicación a las solicitudes, el equilibrador de carga se daría cuenta de que no está obteniendo una respuesta y actuará en consecuencia.

2

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

1) Después de invocar listen(), las conexiones no son realmente aceptadas hasta que (lógicamente) llame a accept(). Puede "desconectarse" simplemente ignorando la actividad de socket y difiriendo cualquier accept() hasta que esté listo para ellos. Cualquier conexión entrante intenta atrasarse en la cola que se creó cuando el puerto se abrió en el modo de escucha. Una vez que la cola de cola de espera está llena en la pila, los intentos de conexión adicionales simplemente se dejan caer en el suelo. Cuando reanude con accept(), rápidamente dequeue la acumulación y estará listo para más conexiones.

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

+0

El problema de mantener esas conexiones en el registro de aceptación() es que aunque usted no las haya aceptado(), el LB ascendente no tiene forma de conocer su cola actual (no solicitudes de procesamiento) Una idea podría ser establecer el backlog a 1, así que una vez que hay una solicitud en cola, las siguientes fallarán –

+0

(¿por qué los comentarios se limitan a 300 caracteres?) para continuar, eso vuelve a poner la responsabilidad en mi servidor para borrar el backlog de accept() regularmente, pero eso es lo que ocurriría de todos modos en condiciones ideales, y en condiciones menos que ideales, el LB que obtiene E_CONNREFUSED evitará las colas contra –

+0

@Dave El otro problema (potencialmente) al aplazar 'accept()' s está en una arquitectura que usa select o epoll desencadenado por nivel, que informará constantemente actividad en el socket de escucha. Supongo que podrías quitar temporalmente el FD del socket de escucha de tu reloj; en mi aplicación, eso aplastaría algunas abstracciones :( –

0

Aquí es un enfoque bastante feo en base a su pregunta Editado:

abrir un socket para escuchar con una cartera normal. Proceder.

Cuando desee "cerrar", abra un segundo con un retraso de 1 y SO_REUSEADDR. Cierra el primero. Cuando esté listo para reanudar, haga otro zócalo de malabares con uno con un retraso acumulado normal.

Los detalles minuciosos sobre el agotamiento de la cola de aceptación del socket que está cerrando serán los asesinos aquí. Probablemente lo suficiente de un asesino para hacer que este enfoque no sea viable.

0

no necesariamente piensan que esto es una buena idea, pero ...

usted podría ser capaz de llamar a escuchar por segunda vez. La especificación POSIX no dice no a. Quizás podría llamarlo por segunda vez con un parámetro de acumulación de 0 cuando desee "desconectar".

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

2

No creo que sea una buena forma de señalizar un equilibrador de carga en sentido ascendente. En realidad, tendría que enviar algunas conexiones a su servidor antes de que el mensaje llegara a su fin; esas conexiones probablemente serían rechazadas.

Del mismo modo, las conexiones que estaban pendientes cuando cerró la toma de escucha se cerrarán sin datos.

Si desea señalizar el balanceador de carga en sentido ascendente, debe tener un protocolo para hacerlo. No intente abusar de TCP para hacerlo.

Afortunadamente, si los clientes son navegadores web normales, puede salirse con la suya con un montón de cosas: simplemente cerrar los sockets generalmente hace que vuelvan a intentar de forma transparente para el usuario (hasta cierto punto).

0

La pregunta no decía qué tipo de socket. Si es un socket unix, puede detenerse y comenzar a escuchar con rename (2). También puede dejar de escuchar permanentemente con unlink (2), y dado que el socket permanece abierto, puede continuar atendiendo su backlog. Este enfoque parece bastante útil, aunque no he visto antes y estoy explorando por mi cuenta.

0

Ya ha obtenido algunas respuestas sobre la imposibilidad de hacerlo a través de la API de socket.

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

He encontrado que la mayoría de los equilibradores de carga son un poco limitado en las posibilidades que ofrecen para reconocer los problemas de conexión (la mayoría de ellos reconocen un RST sólo en la sonda de conexión, no como respuesta a un intento de conexión de fiar.)

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

Cuestiones relacionadas