2010-10-07 13 views
12

Tengo en mi aplicación una falla que surgió que no parece ser reproducible. Tengo una conexión de socket TCP que falló y la aplicación intentó volver a conectarlo. En la segunda llamada a connect() al intentar reconectar, obtuve un resultado de error con errno == EADDRNOTAVAIL que la página man para connect() dice que significa: "La dirección especificada no está disponible en la máquina local".¿Por qué se debería conectar() dar EADDRNOTAVAIL?

Al mirar la llamada a connect(), el segundo argumento parece ser la dirección a la que hace referencia el error, pero según tengo entendido, este argumento es la dirección de socket TCP del host remoto, así que estoy confundido acerca de la página del hombre que se refiere a la máquina local. ¿Es que esta dirección del servidor de socket TCP remoto no está disponible desde mi máquina local? Si es así, ¿por qué sería esto? Tuvo que haber tenido éxito al llamar a connect() la primera vez antes de que fallara la conexión e intentó reconectarse y obtuvo este error. Los argumentos para connect() fueron los mismos en ambas ocasiones.

¿Sería este un error transitorio que, si hubiera tratado de llamar a Connect otra vez, podría haber desaparecido si hubiera esperado lo suficiente? Si no, ¿cómo debo tratar de recuperarme de esta falla?

+0

Tengo un problema similar en un gran cluster de Redis. ¿Cuál es tu caso de uso? El enlace – Riccardo

Respuesta

19

Comprobar este enlace

http://www.toptip.ca/2010/02/linux-eaddrnotavail-address-not.html

EDITAR: Sí intención de añadir más, pero tuvo que cortarlo allí debido a una emergencia

cerraste la toma de corriente antes de intentar volver a conectar? El cierre le indicará al sistema que el par de socket (ip/puerto) ahora está libre.

Éstos son elementos adicionales también mirar:

  • Si el puerto local ya está conectado a la IP remota dada y el puerto (es decir, ya hay una socketpair idénticos), recibirá este error (ver enlace de error a continuación).
  • El enlace de una dirección de socket que no es la local producirá este error. si las direcciones IP de una máquina son 127.0.0.1 y 1.2.3.4, y está intentando vincularse a 1.2.3.5, obtendrá este error.
  • EADDRNOTAVAIL: La dirección especificada no está disponible en la máquina remota o el campo de dirección de la estructura de nombre es todo ceros.

Enlace con un error similar a la suya (la respuesta está cerca de la parte inferior)

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4294599

Parece que el conector está básicamente atrapado en uno de los estados internos del PCT y que la adición de un retraso para la reconexión podría resolver su problema, ya que parecen haber hecho en ese informe de error.

+0

es útil, pero no es solo poner el enlace, especialmente cuando muchos enlaces se vuelven obsoletos e inútiles. –

+0

Ahí va :) buena respuesta, David. – slezica

2

Esto también puede ocurrir si no se da un puerto no válido, al igual que 0.

+2

Como el puerto de destino. Si se proporciona como un puerto local para enlazar, es válido. – EJP

0

Otra cosa a comprobar es que la interfaz está activa. Me confundí recientemente cuando uso espacios de nombres de red, ya que parece que la creación de un nuevo espacio de nombres de red produce una interfaz de bucle invertido completamente independiente, pero no aparece (al menos, con las versiones de cosas de Debian wheezy). Esto se me escapó por un tiempo ya que uno normalmente no piensa que el loopback está abajo.

1

Si no desea cambiar el número de puertos temporales disponibles (como lo sugiere David), o necesita más conexiones que el máximo teórico, existen otros dos métodos para reducir el número de puertos en uso. Sin embargo, son en varios grados las violaciones del estándar TCP, por lo que deben utilizarse con cuidado.

La primera es activar SO_LINGER con un tiempo de espera de cero segundos, forzando a la pila TCP a enviar un paquete RST y purgar el estado de la conexión. Sin embargo, hay una sutileza: debe llamar al shutdown en el descriptor de archivo de socket antes de close, de modo que tenga la oportunidad de enviar un paquete FIN antes del paquete RST. Así que el código se verá algo como:

shutdown(fd, SHUT_RDWR); 
struct linger linger; 
linger.l_onoff = 1; 
linger.l_linger = 0; 
// todo: test for error 
setsockopt(fd, SOL_SOCKET, SO_LINGER, 
      (char *) &linger, sizeof(linger)); 
close(fd); 

El servidor sólo debe ver a restablecer una conexión prematura si el paquete FIN se reordenan con el paquete RST.

Ver TCP option SO_LINGER (zero) - when it's required para más detalles. (Experimentalmente, no parece importar donde se define setsockopt.)

La segunda es el uso de SO_REUSEADDR y un explícito bind (incluso si usted es el cliente), lo que permitirá la reutilización de Linux puertos temporales cuando correr, antes de que terminen de esperar. Tenga en cuenta que debe usar bind con INADDR_ANY y el puerto 0, de lo contrario SO_REUSEADDR no se respeta. Su código será algo como:

int opts = 1; 
// todo: test for error 
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, 
     (char *) &opts, sizeof(int)); 

struct sockaddr_in listen_addr; 
listen_addr.sin_family = AF_INET; 
listen_addr.sin_port = 0; 
listen_addr.sin_addr.s_addr = INADDR_ANY; 
// todo: test for error 
bind(fd, (struct sockaddr *) &listen_addr, sizeof(listen_addr)); 

// todo: test for addr 
// saddr is the struct sockaddr_in you're connecting to 
connect(fd, (struct sockaddr *) &saddr, sizeof(saddr)); 

Esta opción es menos buena, ya que todavía va a saturar las estructuras de datos internas del núcleo para las conexiones TCP como por netstat -an | grep -e tcp -e udp | wc -l. Sin embargo, no comenzará a reutilizar puertos hasta que esto ocurra.

+0

Configurando 'SO_LINGER' a cero resolvió mi problema. Gracias. – Eric

Cuestiones relacionadas