Wie finde ich einen verfügbaren Port?


194

Ich möchte einen Server starten, der einen Port abhört. Ich kann den Port explizit angeben und es funktioniert. Aber ich möchte automatisch einen Port finden. In dieser Hinsicht habe ich zwei Fragen.

  1. In welchem ​​Bereich von Portnummern soll ich suchen? (Ich habe die Ports 12345, 12346 und 12347 verwendet und es war in Ordnung).

  2. Wie kann ich herausfinden, ob ein bestimmter Port nicht von einer anderen Software belegt ist?

Antworten:


277

Wenn Ihnen der verwendete Port nichts ausmacht, geben Sie dem ServerSocket-Konstruktor einen Port von 0 an, der jeden freien Port überwacht .

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

Wenn Sie einen bestimmten Satz von Ports verwenden möchten, ist es wahrscheinlich am einfachsten, diese zu durchlaufen, bis einer funktioniert. Etwas wie das:

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

Könnte so verwendet werden:

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

2
Bei Verwendung von neuem ServerSocket (0) sollte darauf geachtet werden, es zu schließen! Basierend auf javasourcecode.org/html/open-source/eclipse/eclipse-3.5.2/org/… , leicht angepasst in meinem gist.github.com/3429822
vorburger

9
@vorburger, dies auf diese Weise zu tun, ist anfällig für Rennbedingungen. Es ist besser, den Server-Socket sofort abzuhören, als ihn zu öffnen, um einen freien Port zu finden, ihn wieder zu schließen und dann einen am freien Port erneut zu öffnen (zu diesem Zeitpunkt besteht eine geringe Wahrscheinlichkeit, dass etwas anderes dies jetzt abhört Port.)
Graham Edgecombe

7
vereinbart, aber es hängt vom genauen Anwendungsfall ab: In meinem Fall musste ich eine freie Portnummer finden, um sie in eine API zu übertragen (z. B. einen eingebetteten Jetty-Starter für Tests) - die jeweilige API möchte eine Socket-Nummer - keine bereits Server-Socket geöffnet. Es kommt also darauf an.
Vorburger

4
@vorburger Angemessene APIs akzeptieren Null als gültige Portnummer zum Abhören und teilen Ihnen dann den tatsächlichen Port mit, der abgehört wird. Allerdings gibt es nicht viele vernünftige APIs: Viele Programme testen speziell, ssh -D 127.0.0.0:0 ...ob ein Port übergeben wird, und lehnen ihn ab ( ? Nein!), Was wirklich frustrierend ist. Wir mussten eine ganze Reihe von Bibliotheken / Programmen patchen, um sie für uns nutzbar zu machen.
Joker_vD

2
@vorburger Wenn Sie einen Port verwenden, sollten Sie darauf achten, ihn zu schließen. new ServerSocket(0)ist in dieser Hinsicht nicht anders. In Ihrem ersten Link ist überhaupt nichts darüber. Ihr zweiter Link weist lediglich eine schlechte Praxis auf. Sobald Sie die aufgebaut haben , ServerSocketsollten Sie verwenden es, nicht schließen Sie es und versuchen die gleiche Portnummer wieder zu verwenden. All dies ist reine Zeitverschwendung und anfällig für Zeitfensterprobleme.
Marquis von Lorne

57

Wenn Sie dem Konstruktor von ServerSocket 0 als Portnummer übergeben, wird Ihnen ein Port zugewiesen.


Ja, ich denke, es ist die eleganteste Lösung, aber aus bestimmten Gründen funktioniert es in meinem Fall nicht. Aber ich muss noch herausfinden, was genau falsch ist.
Roman

1
@ Roman: Warum funktioniert es nicht? Aktualisieren Sie Ihre Frage, um dies einzuschließen (oder die Leute werden es weiterhin vorschlagen) und erklären Sie, warum diese Lösung für Sie fehlschlägt.
FrustratedWithFormsDesigner

2
Wie finde ich diesen Port vom Client? ;)
ed22

34

Ab Java 1.7 können Sie Try-with-Resources wie folgt verwenden:

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

    }
  }

Wenn Sie einen offenen Port für eine bestimmte Schnittstelle suchen müssen, überprüfen Sie ServerSocket Dokumentation nach alternativen Konstruktoren.

Warnung: Jeder Code, der die von dieser Methode zurückgegebene Portnummer verwendet, unterliegt einer Race-Bedingung. Ein anderer Prozess / Thread kann unmittelbar nach dem Schließen der ServerSocket-Instanz an denselben Port gebunden werden.


1
Dies funktioniert möglicherweise nicht, wenn Sie nicht festlegen SO_REUSEADDR. Und es gibt eine Rennbedingung, aber es ist schwer, das zu beheben.
David Ehrmann

Dies funktioniert möglicherweise nicht, wenn Sie anschließend versuchen, eine andere ServerSocket mit derselben Portnummer zu öffnen , wie dies in der Zwischenzeit möglicherweise geschehen ist. Warum Sie das tatsächliche nicht einfach zurückgeben, ServerSocketanstatt es zu schließen, bleibt ein Rätsel.
Marquis von Lorne

5
@EJP Manchmal akzeptiert Code von Drittanbietern einen Port, aber nicht den Socket selbst.
Captain Man

@CaptainMan Sicher, aber in diesen Fällen wird davon ausgegangen, dass der Port vorab zugewiesen ist. Ein dynamisch zugewiesener Port muss den Clients durch einen anderen Mechanismus wie einen Namensdienst oder einen Broadcast oder Mulitcast bereitgestellt werden. Die ganze Frage hier ist falsch.
Marquis von Lorne

@ user207421 Sie können den Code eines Drittanbieters jedoch nicht so ändern, dass er einen ServerSocketanstelle eines intfür den Port akzeptiert .
Captain Man

30

Laut Wikipedia sollten Sie Ports verwenden, 49152um65535 wenn Sie keinen "bekannten" Port benötigen.

AFAIK Der einzige Weg, um festzustellen, ob ein Port verwendet wird, besteht darin, zu versuchen, ihn zu öffnen.


6
+1. Da Wikipedia nicht immer die Quelle der absoluten Wahrheit / Fakten ist, hielt ich es für nützlich, darauf hinzuweisen, dass die Referenz für diese Wikipedia-Seite von "Internet Assigned Numbers Authority (IANA)" [Dienstname und Transportprotokoll-Portnummer "stammt Registrierung ( Seite iana.org/assignments/service-names-port-numbers/… ", basierend auf RFC 6335 - Abschnitt 6 (dh" Solid " -Referenz in diesem Fall! :)).
OzgurH

25

Wenn Sie in Reichweite benötigen :

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

Ich habe mich gefragt, wie ich einen Port überprüfen und dann von ihm trennen soll. Danke SergeyB!
Vlad Ilie

5
Wenn jeder Port im Bereich [von, bis) verwendet wird, wird dieser Code unendlich wiederholt (oder zumindest bis einer dieser Ports frei wird). Wenn Sie einen sequentiellen Scan durchführen, anstatt zufällig Ports im Bereich auszuwählen, können Sie diese Möglichkeit vermeiden (lösen Sie einfach eine Ausnahme aus, wenn Sie das Ende des Bereichs erreichen, ohne einen freien Port zu finden). Wenn Sie wirklich zufällig Ports auswählen müssen, benötigen Sie einen Satz, um die Ports zu verfolgen, die Sie bisher ausprobiert haben, und dann einen Fehler auszulösen, wenn die Größe dieses Satzes gleich - von ist.
Simon Kissane

Port 0 ist erheblich einfacher und erfordert ServerSocketsim Erfolgsfall nicht das Erstellen von zwei und leidet nicht unter den Zeitfensterproblemen dieses Codes.
Marquis von Lorne




6

Dies funktioniert bei mir unter Java 6

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

6

Ich habe kürzlich eine winzige Bibliothek veröffentlicht, um genau das für Tests zu tun. Die Maven-Abhängigkeit ist:

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

Nach der Installation erhalten Sie freie Portnummern über:

int port = FreePortFinder.findFreeLocalPort();

4

Wenn Ihr Server gestartet wird, wurde dieser Socket nicht verwendet.

BEARBEITEN

Etwas wie:

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

Ich weiß nicht, wer dich abgelehnt hat, aber ich habe dich wieder gewählt. Sie können eine rekursive Funktion einrichten, um zu versuchen, das ServerSocket einzurichten. Wenn Sie eine IOException (oder was auch immer) erhalten, versuchen Sie es erneut, bis ein Port erfolgreich abgerufen wurde.
Jonescb

Ich denke, es ist besser zu überprüfen, ob ein Port verfügbar ist, und dann zu versuchen, diesen Port abzuhören. Es scheint nicht elegant zu sein, etwas zu beginnen und dann herauszufinden, dass es ein Problem gibt, und dann zu versuchen, diese Probleme zu lösen.
Roman

2
@Roman gut, ja, das wäre besser, außer dass es keine Methode gibt, um zu wissen, ob ein Port verfügbar ist. Wenn new ServerSocket(0)es für Sie nicht funktioniert, ist dies die Alternative. Ich denke, es gibt 85% der Möglichkeiten, dass Sie meinen Vorschlag nutzen.
OscarRyz

Dieser Code öffnet und leckt ServerSocketsfür immer und behält nur den letzten , der erfolgreich war. Es braucht eine breakErklärung.
Marquis von Lorne

4

Wenn Sie Ihren eigenen Server mit a erstellen möchten ServerSocket, können Sie ihn einfach einen freien Port für Sie auswählen lassen:

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

Andere Serverimplementierungen unterstützen normalerweise ähnlich. Jetty wählt beispielsweise einen freien Port aus, es sei denn, Sie legen ihn explizit fest:

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

3

Es mag Ihnen nicht viel helfen, aber auf meinem (Ubuntu) Computer habe ich eine Datei / etc / services, in der zumindest die von einigen Apps verwendeten / reservierten Ports angegeben sind. Dies sind die Standardports für diese Apps.

Keine Garantie, dass diese ausgeführt werden, nur die Standardports, die diese Apps verwenden (daher sollten Sie sie nach Möglichkeit nicht verwenden).

Es sind etwas mehr als 500 Ports definiert, etwa die Hälfte UDP und die Hälfte TCP.

Die Dateien werden unter Verwendung von Informationen von IANA erstellt, siehe IANA Zugewiesene Portnummern .


Es gibt eine ähnliche (und vollständigere IIRC) Liste, die Teil von nmap ist. +1
rmeador

3

Wenn Sie Spring verwenden, ist die Antwort von Michail Nikolaev die einfachste und sauberste, und IMO sollte positiv bewertet werden. Aus praktischen Gründen werde ich ein Inline-Beispiel mit der Springframwework SocketUtils .findAvailableTcpPort () -Methode hinzufügen :

int randomAvailablePort = SocketUtils.findAvailableTcpPort();

So einfach ist das, nur eine Zeile :). Natürlich bietet die Utils-Klasse viele andere interessante Methoden. Ich schlage vor, einen Blick auf die Dokumente zu werfen.


1

Mit der Klasse 'ServerSocket' können wir feststellen, ob ein bestimmter Port verwendet wird oder frei ist. ServerSocket bietet einen Konstruktor, der eine Ganzzahl (die Portnummer) als Argument verwendet und den Server-Socket am Port initialisiert. Wenn ServerSocket eine E / A-Ausnahme auslöst, können wir davon ausgehen, dass dieser Port bereits verwendet wird.

Das folgende Snippet wird verwendet, um alle verfügbaren Ports abzurufen.

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

         }
}

Referenz Link .


0

Wenn der Port von einer anderen Software belegt ist, löst der Code eine IOException aus

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.