Comment trouver un port disponible?

201

Je veux démarrer un serveur qui écoute un port. Je peux spécifier le port explicitement et cela fonctionne. Mais j'aimerais trouver un port de manière automatique. A cet égard, j'ai deux questions.

  1. Dans quelle plage de numéros de port dois-je rechercher? (J'ai utilisé les ports 12345, 12346 et 12347 et c'était bien).

  2. Comment puis-je savoir si un port donné n'est pas occupé par un autre logiciel?

romain
la source
si le port est occupé par un autre logiciel, le code lancera une IOException
Cezar Terzian

Réponses:

290

Si le port utilisé ne vous dérange pas, spécifiez un port de 0 au constructeur ServerSocket et il écoutera sur n'importe quel port libre.

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

Si vous souhaitez utiliser un ensemble spécifique de ports, le moyen le plus simple est probablement de les parcourir jusqu'à ce que l'un d'entre eux fonctionne. Quelque chose comme ça:

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");
}

Pourrait être utilisé comme ceci:

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
la source
2
Lorsque vous utilisez le nouveau ServerSocket (0), veillez à le fermer! Basé sur javasourcecode.org/html/open-source/eclipse/eclipse-3.5.2/org/… , légèrement adapté dans mon gist.github.com/3429822
vorburger
10
@vorburger, le faire de cette manière est sujet aux conditions de course. Il est plus agréable d'écouter immédiatement sur le socket du serveur, plutôt que de l'ouvrir pour trouver un port libre, de le fermer à nouveau, puis de l'ouvrir à nouveau sur le port libre (à ce moment, il y a une petite chance que quelque chose d'autre écoute maintenant sur cela port.)
Graham Edgecombe
7
d'accord, mais cela dépend du cas d'utilisation exact: dans mon cas, je devais trouver un numéro de port libre pour le transmettre à une API (par exemple un démarreur Jetty intégré, pour les tests) - l'API respective veut un numéro de socket - pas déjà socket serveur ouvert. Cela dépend donc.
vorburger le
4
Les API @vorburger Reasonable accepteront zéro comme numéro de port valide pour écouter, puis vous indiqueront le port réel écouté. Hovewer, il n'y a pas beaucoup d'API raisonnables: de nombreux programmes testent spécifiquement le port 0 et le refusent ( ssh -D 127.0.0.0:0 ...? Non!), Ce qui est vraiment frustrant. Nous avons dû patcher un certain nombre de bibliothèques / programmes pour les rendre utiles.
Joker_vD
2
@vorburger Lorsque vous utilisez un port, veillez à le fermer. new ServerSocket(0)n'est pas différent à cet égard. Il n'y a rien du tout dans votre premier lien à ce sujet. Votre deuxième lien montre simplement une mauvaise pratique. Une fois que vous avez construit le, ServerSocketvous devez l' utiliser , ne pas le fermer et essayer de réutiliser le même numéro de port. Tout cela est une perte de temps totale, en plus d'être vulnérable aux problèmes de fenêtre temporelle.
Marquis of Lorne
57

Si vous passez 0 comme numéro de port au constructeur de ServerSocket, il allouera un port pour vous.

Maurice Perry
la source
Oui, je pense que c'est la solution la plus élégante mais pour certaines raisons, cela ne fonctionne pas dans mon cas. Mais j'ai encore besoin de savoir ce qui ne va pas exactement.
Roman
1
@Roman: Pourquoi ça ne marche pas? Mettez à jour votre question pour l'inclure (ou les gens continueront de la suggérer) et expliquez pourquoi cette solution échoue pour vous.
FrustratedWithFormsDesigner
4
Comment trouver ce port à partir du client? ;)
ed22
36

À partir de Java 1.7, vous pouvez utiliser try-with-resources comme ceci:

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

    }
  }

Si vous avez besoin de trouver un port ouvert sur une interface spécifique, consultez la documentation de ServerSocket pour d'autres constructeurs.

Avertissement: tout code utilisant le numéro de port renvoyé par cette méthode est soumis à une condition de concurrence - un processus / thread différent peut se lier au même port immédiatement après la fermeture de l'instance ServerSocket.

Andrei Savu
la source
1
Cela pourrait ne pas fonctionner si vous ne définissez pas SO_REUSEADDR. Et il y a une condition de course, mais il est difficile de résoudre ce problème.
David Ehrmann le
Cela peut ne pas fonctionner si vous essayez par la suite d'en ouvrir un autre ServerSocket avec le même numéro de port, car il aurait pu être utilisé entre-temps. Pourquoi ne pas simplement retourner le réel ServerSocketau lieu de le fermer reste un mystère.
Marquis of Lorne
5
@EJP Parfois, un code tiers accepte un port mais pas le socket lui-même.
Captain Man
@CaptainMan Bien sûr, mais dans ces cas, le port est sûrement supposé être préalloué. Un port alloué dynamiquement doit être fourni aux clients par un autre mécanisme tel qu'un service de dénomination ou une diffusion ou une diffusion multiple. Toute la question ici est mal formulée.
Marquis de Lorne
@ user207421 Vous ne pouvez pas changer le code tiers pour accepter un ServerSocketau lieu d'un intpour le port.
Captain Man
32

Selon Wikipédia , vous devez utiliser les ports 49152pour 65535si vous n'avez pas besoin d' un port « bien connu ».

AFAIK, la seule façon de déterminer si un port est utilisé est d'essayer de l'ouvrir.

Andy Johnson
la source
7
+1. Étant donné que Wikipédia n'est pas toujours la source de la vérité / des faits absolus, j'ai pensé qu'il pourrait être utile de noter que la référence de cette page Wikipédia provient de "Internet Assigned Numbers Authority (IANA)" s "[Nom du service et numéro de port du protocole de transport Registry ( iana.org/assignments/service-names-port-numbers/… "page, basée sur RFC 6335 - Section 6 (c'est-à-dire référence" solide "dans ce cas! :)).
OzgurH
25

Si vous avez besoin d' une utilisation dans la gamme :

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
la source
se demandait comment vérifier un port, puis en dissocier. Merci SergeyB!
Vlad Ilie
6
Si chaque port de la plage [de, à) est utilisé, ce code bouclera indéfiniment (ou du moins jusqu'à ce que l'un de ces ports devienne libre). Si vous effectuez une analyse séquentielle plutôt que de choisir les ports de la plage au hasard, vous pouvez éviter cette possibilité (il suffit de lancer une exception lorsque vous arrivez à la fin de la plage sans trouver de port libre). Si vous avez vraiment besoin de choisir des ports au hasard, vous avez besoin d'un ensemble pour garder une trace des ports que vous avez essayés jusqu'à présent, puis générer une erreur lorsque la taille de cet ensemble est égale à - de.
Simon Kissane
Le port 0 est considérablement plus simple et ne nécessite pas d'en créer deux ServerSocketsen cas de succès, et ne souffre pas des problèmes de fenêtre de synchronisation de ce code.
Marquis de Lorne
11

Le SDK Eclipse contient une classe SocketUtil , qui fait ce que vous voulez. Vous pouvez consulter le code source de git .

jopa
la source
2
En regardant le code d'Eclipse, ils font la même chose que la réponse de Graham Edgecombe
KevinL
7

Cela fonctionne pour moi sur Java 6

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

J'ai récemment publié une petite bibliothèque pour faire exactement cela avec des tests à l'esprit. La dépendance Maven est:

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

Une fois installés, les numéros de port gratuits peuvent être obtenus via:

int port = FreePortFinder.findFreeLocalPort();
Alex Panov
la source
6

Voir ServerSocket :

Crée un socket serveur, lié au port spécifié. Un port de 0 crée un socket sur n'importe quel port libre.

f1sh
la source
4

Si votre serveur démarre, ce socket n'a pas été utilisé.

ÉDITER

Quelque chose comme:

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
la source
Je ne sais pas qui vous a voté contre, mais je vous ai voté de nouveau. Vous pouvez configurer une fonction récursive pour essayer de configurer le ServerSocket, et si vous obtenez une IOException (ou quoi que ce soit), vous essayez à nouveau jusqu'à ce qu'il obtienne avec succès un port.
jonescb
Je pense qu'il vaut mieux vérifier si un port est disponible, puis essayer de commencer à écouter ce port. Il ne semble pas être élégant de commencer quelque chose et de découvrir ensuite qu'il y a un problème et d'essayer de résoudre ces problèmes.
Roman
2
@Roman eh bien, oui, ce serait mieux, sauf pour le fait qu'il n'y a pas de méthode pour savoir si un port est disponible. Si cela new ServerSocket(0)ne fonctionne pas pour vous, l'alternative est la suivante. Je pense qu'il y a 85% des possibilités que vous finissez par utiliser ma suggestion.
OscarRyz
Ce code ne cesse de s'ouvrir et de fuir ServerSocketspour toujours et ne conserve que le dernier qui a réussi. Il faut une breakdéclaration.
Marquis de Lorne
4

Si vous souhaitez créer votre propre serveur en utilisant a ServerSocket, vous pouvez simplement lui demander de choisir un port gratuit pour vous:

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

D'autres implémentations de serveur ont généralement un support similaire. Jetty, par exemple, choisit un port libre à moins que vous ne le définissiez explicitement:

  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
la source
3

Cela peut ne pas vous aider beaucoup, mais sur ma machine (Ubuntu), j'ai un fichier / etc / services dans lequel au moins les ports utilisés / réservés par certaines des applications sont donnés. Ce sont les ports standard pour ces applications.

Aucune garantie que celles-ci fonctionnent, juste les ports par défaut que ces applications utilisent (vous ne devez donc pas les utiliser si possible).

Il y a un peu plus de 500 ports définis, environ la moitié UDP et la moitié TCP.

Les fichiers sont créés à l'aide d'informations fournies par l'IANA, voir Numéros de port attribués par l'IANA .

extraneon
la source
il existe une liste similaire (et plus complète, IIRC) qui fait partie de nmap. +1
rmeador
3

Si vous utilisez Spring, la réponse fournie par Michail Nikolaev est la plus simple et la plus propre, et l'OMI devrait être votée pour. Juste pour des raisons de commodité, j'ajouterai un exemple en ligne en utilisant la méthode Springframwework SocketUtils .findAvailableTcpPort () :

int randomAvailablePort = SocketUtils.findAvailableTcpPort();

C'est aussi simple que ça, juste une ligne :). Bien sûr, la classe Utils offre de nombreuses autres méthodes intéressantes, je suggère de jeter un œil à la documentation.

Gerardo Roza
la source
1

En utilisant la classe «ServerSocket», nous pouvons identifier si un port donné est utilisé ou libre. ServerSocket fournit un constructeur qui prend un entier (qui est le numéro de port) comme argument et initialise le socket serveur sur le port. Si ServerSocket lève une exception IO, nous pouvons supposer que ce port est déjà utilisé.

L'extrait suivant est utilisé pour obtenir tous les ports disponibles.

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

         }
}

Lien de référence .

Hari Krishna
la source
0

Si vous utilisez Spring Framework, le moyen le plus simple de le faire est:

private Integer laancNotifyPort = SocketUtils.findAvailableTcpPort();

Vous pouvez également définir une plage acceptable, et il recherchera dans cette plage:

private Integer laancNotifyPort = SocketUtils.findAvailableTcpPort(9090, 10090);

Il s'agit d'une méthode pratique qui évite la complexité, mais en interne, elle est similaire à de nombreuses autres réponses sur ce fil.

anataliocs
la source