¿Cómo encontrar un puerto disponible?

194

Quiero iniciar un servidor que escuche un puerto. Puedo especificar el puerto explícitamente y funciona. Pero me gustaría encontrar un puerto de forma automática. A este respecto tengo dos preguntas.

  1. ¿En qué rango de números de puerto debo buscar? (Usé los puertos 12345, 12346 y 12347 y estuvo bien).

  2. ¿Cómo puedo saber si un puerto determinado no está ocupado por otro software?

romano
fuente

Respuestas:

277

Si no le importa el puerto utilizado, especifique un puerto de 0 para el constructor ServerSocket y escuchará en cualquier puerto libre.

ServerSocket s = new ServerSocket(0);
System.out.println("listening on port: " + s.getLocalPort());

Si desea utilizar un conjunto específico de puertos, entonces la forma más fácil es iterar a través de ellos hasta que uno funcione. Algo como esto:

public ServerSocket create(int[] ports) throws IOException {
    for (int port : ports) {
        try {
            return new ServerSocket(port);
        } catch (IOException ex) {
            continue; // try next port
        }
    }

    // if the program gets here, no port in the range was found
    throw new IOException("no free port found");
}

Podría usarse así:

try {
    ServerSocket s = create(new int[] { 3843, 4584, 4843 });
    System.out.println("listening on port: " + s.getLocalPort());
} catch (IOException ex) {
    System.err.println("no available ports");
}
Graham Edgecombe
fuente
2
Cuando utilice el nuevo ServerSocket (0), ¡tenga cuidado de cerrarlo! Basado en javasourcecode.org/html/open-source/eclipse/eclipse-3.5.2/org/… , ligeramente adaptado en mi gist.github.com/3429822
vorburger
9
@vorburger, hacerlo de esa manera es propenso a las condiciones de carrera. Es mejor escuchar en el zócalo del servidor de inmediato, en lugar de abrirlo para encontrar un puerto libre, cerrarlo nuevamente y luego abrir uno nuevamente en el puerto libre (en ese momento hay una pequeña posibilidad de que algo más esté escuchando en ese momento puerto.)
Graham Edgecombe
77
acordado, pero depende del caso de uso exacto: en mi caso, necesitaba encontrar un número de puerto libre para entregarlo en alguna API (por ejemplo, un iniciador de Jetty incorporado, para pruebas), la API respectiva quiere un número de socket, no uno ya Socket de servidor abierto. Entonces eso depende.
vorburger
44
@vorburger Las API razonables aceptarán cero como un número de puerto válido para escuchar, y luego le indicarán el puerto real que se está escuchando. Sin embargo, no hay muchas API razonables: muchos programas prueban específicamente el paso de 0 puertos y lo rechazan ( ssh -D 127.0.0.0:0 ...¡No!), Lo cual es realmente frustrante. Tuvimos que parchear un buen número de bibliotecas / programas para que fueran útiles para nosotros.
Joker_vD
2
@vorburger Al usar cualquier puerto, se debe tener cuidado para cerrarlo. new ServerSocket(0)No es diferente a este respecto. No hay nada en su primer enlace sobre esto. Su segundo enlace simplemente muestra malas prácticas. Una vez que haya construido el ServerSocket, debe usarlo , no cerrarlo e intentar reutilizar el mismo número de puerto. Todo eso es una pérdida de tiempo completa, además de ser vulnerable a los problemas de la ventana de tiempo.
Marqués de Lorne
57

Si pasa 0 como número de puerto al constructor de ServerSocket, le asignará un puerto.

Maurice Perry
fuente
Sí, creo que es la solución más elegante, pero por alguna razón no funciona en mi caso. Pero aún necesito averiguar qué es exactamente lo que está mal.
Romano
1
@Roman: ¿Por qué no funciona? Actualice su pregunta para incluir esto (o la gente seguirá sugiriéndola) y explique por qué esta solución le falla.
FrustratedWithFormsDesigner
2
¿Cómo encuentro este puerto del cliente? ;)
ed22
34

A partir de Java 1.7, puede usar probar con recursos como este:

  private Integer findRandomOpenPortOnAllLocalInterfaces() throws IOException {
    try (
        ServerSocket socket = new ServerSocket(0);
    ) {
      return socket.getLocalPort();

    }
  }

Si necesita encontrar un puerto abierto en una interfaz específica, consulte la documentación de ServerSocket para buscar constructores alternativos.

Advertencia: cualquier código que use el número de puerto devuelto por este método está sujeto a una condición de carrera: un proceso / subproceso diferente puede unirse al mismo puerto inmediatamente después de cerrar la instancia de ServerSocket.

Andrei Savu
fuente
1
Esto podría no funcionar si no lo configura SO_REUSEADDR. Y hay una condición de carrera, pero es difícil solucionarlo.
David Ehrmann
Esto podría no funcionar si posteriormente intenta abrir otro ServerSocket con el mismo número de puerto, ya que podría haberse tomado mientras tanto. Por qué no solo devolverías lo real en ServerSocketlugar de cerrarlo sigue siendo un misterio.
Marqués de Lorne
55
@EJP A veces, el código de terceros acepta un puerto pero no el socket en sí.
Capitán Man
@CaptainMan Claro, pero en esos casos se supone que el puerto está previamente asignado. Un puerto asignado dinámicamente tiene que ser suministrado a los clientes por algún otro mecanismo, como un servicio de nombres o una difusión o difusión múltiple. Toda la pregunta aquí está mal formada.
Marqués de Lorne el
@ user207421 Sin embargo, no puede cambiar el código de terceros para aceptar un en ServerSocketlugar de un intpara el puerto.
Capitán Man
30

De acuerdo a Wikipedia , se debe utilizar puertos 49152a 65535si que no es necesario un puerto 'conocida'.

AFAIK, la única forma de determinar si un puerto está en uso es intentar abrirlo.

Andy Johnson
fuente
66
+1. Como Wikipedia no siempre es la fuente de la verdad o los hechos absolutos, pensé que podría ser útil tener en cuenta que la referencia de esa página de Wikipedia proviene de "Autoridad de Números Asignados de Internet (IANA)" s "[Nombre del servicio y número de puerto del protocolo de transporte Registro ( iana.org/assignments/service-names-port-numbers/… ", basado en RFC 6335 - Sección 6 (es decir, ¡referencia" sólida "en este caso! :)).
OzgurH
25

Si necesita en el rango de uso:

public int nextFreePort(int from, int to) {
    int port = randPort(from, to);
    while (true) {
        if (isLocalPortFree(port)) {
            return port;
        } else {
            port = ThreadLocalRandom.current().nextInt(from, to);
        }
    }
}

private boolean isLocalPortFree(int port) {
    try {
        new ServerSocket(port).close();
        return true;
    } catch (IOException e) {
        return false;
    }
}
Serhii Bohutskyi
fuente
se preguntaba cómo verificar un puerto y luego desconectarlo. Gracias SergeyB!
Vlad Ilie
55
Si todos los puertos en el rango [desde, hasta) están en uso, este código se repetirá infinitamente (o al menos hasta que uno de esos puertos quede libre). Si realiza un escaneo secuencial en lugar de seleccionar puertos en el rango al azar, puede evitar esta posibilidad (solo arroje una excepción cuando llegue al final del rango sin encontrar un puerto libre). Si realmente necesita elegir puertos al azar, entonces necesita un conjunto para realizar un seguimiento de los puertos que ha probado hasta ahora, y luego generar un error cuando el tamaño de ese conjunto es igual a - desde.
Simon Kissane
El puerto 0 es considerablemente más simple y no requiere crear dos ServerSocketsen el caso de éxito, y no sufre los problemas de ventana de tiempo de este código.
Marqués de Lorne el
11

El SDK de Eclipse contiene una clase SocketUtil , que hace lo que desea. Puede echar un vistazo al código fuente de git .

jopa
fuente
2
Al mirar el código de Eclipse, hacen lo mismo que la respuesta de Graham Edgecombe
KevinL
6

Esto funciona para mí en Java 6

    ServerSocket serverSocket = new ServerSocket(0);
    System.out.println("listening on port " + serverSocket.getLocalPort());
Peter Lawrey
fuente
6

Recientemente he lanzado una pequeña biblioteca para hacer precisamente eso con las pruebas en mente. La dependencia de Maven es:

<dependency>
    <groupId>me.alexpanov</groupId>
    <artifactId>free-port-finder</artifactId>
    <version>1.0</version>
</dependency>

Una vez instalado, se pueden obtener números de puerto libres a través de:

int port = FreePortFinder.findFreeLocalPort();
Alex Panov
fuente
4

Si su servidor se inicia, entonces ese socket no se usó.

EDITAR

Algo como:

ServerSocket s = null ;

try { 
    s = new ServerSocket( 0 ); 
} catch( IOException ioe ){
   for( int i = START; i < END ; i++ ) try {
        s = new ServerSocket( i );
    } catch( IOException ioe ){}
}
// At this point if s is null we are helpless
if( s == null ) {
    throw new IOException(
       Strings.format("Unable to open server in port range(%d-%d)",START,END));
}
OscarRyz
fuente
No sé quién te rechazó, pero yo te voté de nuevo. Puede configurar una función recursiva para intentar configurar el ServerSocket, y si obtiene una IOException (o lo que sea), intente nuevamente hasta que obtenga un puerto con éxito.
jonescb
Creo que es mejor verificar si hay un puerto disponible y luego intentar comenzar a escucharlo. No parece ser elegante comenzar algo y luego descubrir que hay algún problema y luego tratar de resolverlos.
Romano
2
@Roman bueno, sí, eso sería mejor, excepto por el hecho de que no hay un método para saber si hay un puerto disponible. Si new ServerSocket(0)no funciona para usted, la alternativa es esta. Creo que hay un 85% de posibilidades de que termines usando mi sugerencia.
OscarRyz
Este código sigue abriéndose y goteando ServerSocketspara siempre y solo retiene el último que tuvo éxito. Necesita una breakdeclaración.
Marqués de Lorne el
4

Si desea crear su propio servidor utilizando un ServerSocket, puede hacer que elija un puerto libre para usted:

  ServerSocket serverSocket = new ServerSocket(0);
  int port = serverSocket.getLocalPort();

Otras implementaciones de servidor suelen tener un soporte similar. Jetty, por ejemplo, elige un puerto libre a menos que lo configure explícitamente:

  Server server = new Server();
  ServerConnector connector = new ServerConnector(server);
  // don't call: connector.setPort(port);
  server.addConnector(connector);
  server.start();
  int port = connector.getLocalPort();
oberlies
fuente
3

Puede que no lo ayude mucho, pero en mi máquina (Ubuntu) tengo un archivo / etc / services en el que se proporcionan al menos los puertos utilizados / reservados por algunas de las aplicaciones. Estos son los puertos estándar para esas aplicaciones.

No hay garantías de que se estén ejecutando, solo los puertos predeterminados que usan estas aplicaciones (por lo que no debe usarlas si es posible).

Hay un poco más de 500 puertos definidos, aproximadamente la mitad de UDP y la mitad de TCP.

Los archivos se crean utilizando información de IANA; consulte los números de puerto asignados de IANA .

extraneon
fuente
Hay una lista similar (y más completa, IIRC) que viene como parte de nmap. +1
rmeador
3

Si está utilizando Spring, la respuesta proporcionada por Michail Nikolaev es la más simple y limpia, y la OMI debe ser votada. Solo por conveniencia, agregaré un ejemplo en línea usando el método Springframwework SocketUtils .findAvailableTcpPort () :

int randomAvailablePort = SocketUtils.findAvailableTcpPort();

Es tan fácil como eso, solo una línea :). Por supuesto, la clase Utils ofrece muchos otros métodos interesantes, sugiero echar un vistazo a los documentos.

Gerardo Roza
fuente
1

Usando la clase 'ServerSocket' podemos identificar si el puerto dado está en uso o libre. ServerSocket proporciona un constructor que toma un número entero (que es el número de puerto) como argumento e inicializa el socket del servidor en el puerto. Si ServerSocket arroja alguna excepción de E / S, entonces podemos suponer que este puerto ya está en uso.

El siguiente fragmento se utiliza para obtener todos los puertos disponibles.

for (int port = 1; port < 65535; port++) {
         try {
                  ServerSocket socket = new ServerSocket(port);
                  socket.close();
                  availablePorts.add(port);
         } catch (IOException e) {

         }
}

Referencia enlace .

Hari Krishna
fuente
0

Si el puerto está ocupado por otro software, el código arrojará una IOException

Cezar Terzian
fuente