2010-07-16 19 views
6

entiendo que el siguiente código puede (quizás no es muy eficiente) descubrir un puerto TCP libre en Java:Encontrar dos puertos TCP gratuitas

public static int findFreePort() { 
    int port; 
    try { 
     ServerSocket socket= new ServerSocket(0); 
     port = socket.getLocalPort(); 
     socket.close(); 
    } catch (Exception e) { port = -1; } 
    return port;  
    } 

(Hay algunas preguntas relacionadas aquí en SO - forexample) .

Lo que no entiendo es por qué (o si) dos llamadas sucesivas a este método tienen la garantía de devolver dos puertos diferentes. Esto se supone, por ejemplo, here (busque las llamadas al método findFreePort).

¿Es esto correcto?

+0

Si tiene preguntas relacionadas, debería citarlas y enlazar a ellas. – Freiheit

+0

@Freiheit: Hecho – leonbloy

+0

No se reasigna debido a SO_WAIT, un mecanismo diseñado para eludir que un paquete aún en tránsito sea recibido por otro proceso.De forma predeterminada, si cierra un puerto TCP, no se reasigna durante los próximos 2 minutos para permitir que estos paquetes persistentes se vacíen. –

Respuesta

5

En la especificación de Javadoc, no veo ninguna línea diciendo que dos llamadas sucesivas está garantizado para devolver dos puertos diferentes ...

Desde el ServerSocket está cerrada, una segunda llamada podría dar el mismo puerto. Estadísticamente, es poco probable pero no imposible, creo.

Si abre sus dos ServerSocket, obtiene los puertos y luego cierra sus dos ServerSocket, tiene garantizado que obtendrá dos puertos diferentes (ya que el primero no es gratuito cuando crea el segundo ServerSocket).

Ejemplo método para obtener n diferentes puertos libres:

public int[] getFreePorts(int portNumber) throws IOException { 
    int[] result = new int[portNumber]; 
    List<ServerSocket> servers = new ArrayList<ServerSocket>(portNumber); 
    ServerSocket tempServer = null; 

    for (int i=0; i<portNumber; i++) { 
     try { 
      tempServer = new ServerSocket(0); 
      servers.add(tempServer); 
      result[i] = tempServer.getLocalPort(); 
     } finally { 
      for (ServerSocket server : servers) { 
       try { 
        server.close(); 
       } catch (IOException e) { 
        // Continue closing servers. 
       } 
      } 
     } 
    } 
    return result; 
} 
+0

Sí, acabo de codificar casi exactamente el mismo método :-) – leonbloy

+1

Cabe señalar que incluso esta solución (y la de Noel M) no es 100% infalible, existe una posible condición de carrera. Después de esta llamada al método, la persona que llama intentará eventualmente usar esos puertos disponibles. Pero podría suceder que, mientras tanto, algún otro proceso lo haya abierto. – leonbloy

0

El código fuente para ServerSocket está aquí: http://kickjava.com/src/java/net/ServerSocket.java.htm

No estoy bastante ver cómo se determina si el puerto está libre o no, pero:

@param port the port number, or <code>0</code> to use any 
free port. 

Así que una vez se asigna el primer puerto, incluso para su aplicación, ya no es gratis. Entonces, las llamadas sucesivas a ServerSocket no reutilizarán ese puerto, garantizando así dos puertos diferentes.

+0

Pero 'findFreePort' abre el socket y lo cierra de inmediato, por lo que en teoría está nuevamente disponible. – leonbloy

+0

En el código, el puerto está asignado, pero el método "close()" desasigna este puerto. Entonces, puede ser reutilizado (por otro ServerSocket, por ejemplo). Entonces, creo que otra llamada a ese método podría dar el mismo número de puerto. –

2

Una forma de obtener dos números de puerto diferentes:

ServerSocket socket1= new ServerSocket(0); 
    port1 = socket.getLocalPort(); 
    ServerSocket socket2= new ServerSocket(0); 
    port2 = socket.getLocalPort(); 

    socket1.close(); 
    socket2.close(); 
0

es tan eficiente como el sistema operativo puede hacer que sea. Sin embargo, cerrar el ServerSocket inmediatamente después no tiene sentido, ya que ese puerto ya no está reservado y puede asignarse a otra cosa. El único propósito de este ejercicio es crear un ServerSocket, así que simplemente créelo.

0

Aquí hay una clase que he usado para encontrar varios puertos libres. Proporciona la flexibilidad de asignar puertos individuales en el flujo de alguna lógica compleja (es decir, cuando tal vez la cantidad de puertos que necesitará no sea un número simple sino que dependa de una lógica compleja). Todavía garantiza que todos los puertos que solicite sean gratuitos y únicos (siempre y cuando utilice la misma instancia de esta clase para obtener todos los puertos).

Así que la forma de usar esta clase sería crear una instancia. Ejecute su código para hacer lo que quiera asignar puertos para usar esa instancia. Luego, una vez que haya vinculado todos los puertos, puede deshacerse de esta instancia y usar una nueva la próxima vez.

public class PortFinder { 

/** 
* If you only need the one port you can use this. No need to instantiate the class 
*/ 
public static int findFreePort() throws IOException { 
    ServerSocket socket = new ServerSocket(0); 
    try { 
     return socket.getLocalPort(); 
    } finally { 
     try { 
      socket.close(); 
     } catch (IOException e) { 
     } 
    } 
} 

private HashSet<Integer> used = new HashSet<Integer>(); 

/** 
* Finds a port that is currently free and is guaranteed to be different from any of the 
* port numbers previously returned by this PortFinder instance. 
*/ 
public synchronized int findUniqueFreePort() throws IOException { 
    int port; 
    do { 
     port = findFreePort(); 
    } while (used.contains(port)); 
    used.add(port); 
    return port; 
} 

} 
+0

donde se encuentra findFreePort(); método. @Kris – Roster

+0

Está justo ahí, el primer miembro de la clase PortFinder. Tal vez confundí las cosas por el orden inusual de ponerlo antes de la declaración de campo. – Kris

Cuestiones relacionadas