2010-03-16 12 views
5

Tengo el siguiente problema. El programa de mi cliente supervisa la disponibilidad del servidor en la red local (usando Bonjour, pero no funciona). Tan pronto como un servidor es "notado" por la aplicación cliente, el cliente intenta crear un socket: Socket(serverIP,serverPort);.¿Debo cerrar los enchufes de ambos extremos?

En algún momento el cliente puede perder el servidor (Bonjour dice que el servidor ya no está visible en la red). Entonces, el cliente decide close el socket, porque ya no es válido.

En algún momento, el servidor vuelve a aparecer. Entonces, el cliente intenta crear un nuevo socket asociado con este servidor. ¡Pero! El servidor puede negarse a crear este socket ya que (el servidor) ya tiene un socket asociado con el IP del cliente y el puerto del cliente. Sucede porque el socket fue cerrado por el cliente, no por el servidor. ¿Puede pasar? Y si es el caso, ¿cómo se puede resolver este problema?

Bueno, entiendo que es poco probable que el cliente intente conectarse al servidor desde el mismo puerto (puerto cliente), ya que el cliente selecciona sus puertos al azar. Pero aún puede suceder (por casualidad). ¿Derecha?

Respuesta

1

Respuesta corta: sí, debe cerrar el zócalo en ambos extremos.

Aunque la respuesta es simple, en realidad puede ser muy difícil detectar que el par ha dejado de responder si no construye algún esquema de ACK/NACK en su protocolo de cliente-servidor.

Incluso con sus ACK de protocolo, su cadena de procesamiento puede estar colgando esperando ACK que nunca vendrán del cliente o viceversa.

Si utiliza bloqueo de E/S, me gustaría empezar ajustando los tiempos de espera de lectura en un socket. Desafortunadamente, si el par deja de responder, no hay un tiempo de espera correspondiente para las escrituras. Un instrumento contundente que encontré que tiene valor en nuestro entorno es crear Sockets de bloqueo a través de los métodos java.nio y luego interrumpir el hilo de procesamiento a intervalos configurables.

La interrupción del hilo de proceso cerrará el socket, pero si elige el tiempo de espera lo suficientemente grande sabrá que hay un problema. Elegimos este enfoque porque la aplicación se escribió inicialmente con bloqueo de E/S y el costo de transición a no-bloqueo fue muy alto.

Sin embargo, con la E/S sin bloqueo, puede verificar en un intervalo más detallado el estado de su conexión y reaccionar de forma más inteligente a las conexiones lentas/que no responden.

Aunque las E/S sin bloqueo requieren una mayor inversión inicial, creo que pagará mejores dividendos en términos de confiabilidad y un mejor rendimiento más adelante. sistema operativo

1

el cliente no asignará el mismo puerto a una nueva toma tan pronto. hay varios mecanismos que lo previenen. uno de los cuales es el estado TIME_WAIT que reserva el puerto durante un tiempo después de que se cierra la conexión.

yo no me preocuparía por eso.

si realmente necesita detectar la desconexión, deberá implementar el protocolo ping/pong, iniciado tanto por el cliente como por el servidor.

2

Sí, cerrar la toma de corriente, tan pronto como se detecte una avería.

El zócalo será "atascado" en "close_wait" si no se ha cerrado correctamente. Incluso si el socket está cerrado, su estado estará en time_wait por un corto período.

Sin embargo, si diseña la aplicación para utilizar un puerto local diferente para cada conexión nueva, no hay necesidad de esperar a que se cierre el socket anterior.

(Como va a crear entonces una toma completamente diferente, ya que un enchufe se identifica mediante el mando a distancia en IP, puerto remoto, IP local y puerto local.)

+1

'Si el diseño de la aplicación para utilizar un puerto local diferente para cada nueva conexión' Como eso es lo que sucede automáticamente, no hay necesidad de diseñar para él. Solo evite especificar puertos locales completamente al construir Sockets. – EJP

+0

Oh, sí, lo que quise decir es que uno no debe diseñar una aplicación para depender de qué puerto proviene una solicitud. – KarlP

1

Parece que su cliente es la detección de pérdida de conectividad al servidor (usando Bonjour), pero no tiene la capacidad correspondiente en la otra dirección.

Seguramente va a necesitar algún tipo de tiempo de espera para las conexiones inactivas en el lado del servidor, de lo contrario las conexiones muertas permanecerán para siempre. Más allá del problema de posibles colisiones de direcciones IP/puertos que menciona, también está el hecho de que las conexiones muertas consumen recursos del sistema operativo y aplicaciones (como los descriptores de archivos abiertos)

Por el contrario, también puede considerar no ser también agresivo al cerrar una conexión desde el lado del cliente cuando Bonjour dice que el servicio ya no es visible. Si se encuentra en un escenario inalámbrico, una pérdida transitoria de conectividad no es poco común, y es posible que una conexión TCP permanezca abierta y válida después de la restauración de la conectividad (suponiendo que el cliente aún tenga la misma dirección IP). La estrategia óptima depende del tipo de conexión de la que está hablando. Si es una conexión relativamente sin estado donde el costo de descartar la conexión y volver a intentarlo es bajo (como HTTP), entonces tiene sentido tirar la conexión en el primer signo de problema. Pero si se trata de una conexión de larga duración con un estado de usuario significativo (como una sesión de inicio de sesión SSH), tiene sentido esforzarse más para mantener la conexión activa.

2

Una ilustración rápida/sucio de por qué esto no puede suceder (nótese el cliente utiliza la fuerza el mismo puerto local en su conexión):

public class Server{ 

public static void main(String[] args) throws Exception { 
    new Thread(){ 
     java.net.ServerSocket server = new java.net.ServerSocket(12345); 
     java.util.ArrayList<java.net.Socket> l = new java.util.ArrayList<java.net.Socket>(); 
     public void run() { 
      try{ 
      while(true){ 
       java.net.Socket client = server.accept(); 
       System.out.println("Connection Accepted: S: "+client.getLocalPort()+", C: "+client.getPort()); 
       l.add(client); 
      } 
      }catch(Exception e){e.printStackTrace();} 
     } 
    }.start(); 
} 

y un cliente (en lugar de la dirección del servidor con algo válido):

import java.net.InetAddress; 
import java.net.Socket; 


public class SocketTest { 
    public static void main(String[] args) throws Exception { 
     InetAddress server = InetAddress.getByName("192.168.0.256"); 
     InetAddress localhost = InetAddress.getLocalHost(); 
     Socket s = new Socket(server, 12345, localhost, 54321); 
     System.out.println("Client created socket"); 
     s.close(); 
     s = null; 
     System.gc(); 
     System.gc(); 
     Thread.sleep(1000); 
     s = new Socket(server, 12345, localhost, 54321); 
     System.out.println("Client created second socket"); 
     s.close(); 
     System.exit(55); 
    } 
} 

Si se inicia el servidor y luego intenta ejecutar el cliente de la primera conexión tendrá éxito, pero el segundo se producirá un error con un "java.net.BindException: Dirección ya en uso: conectar"

-1

Si cierra el socket del servidor solo en el caso de un socket de bloqueo, entonces el socket del cliente se cerrará, pero no al revés.

de lo contrario sería mejor socket en ambos extremos. Porque socket es un peso pesado para su sistema. Utilizará un puerto local y un puerto remoto de su sistema para siempre.

Gracias Sunil Kumar sahoo

Cuestiones relacionadas