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?

römisch
quelle

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");
}
Graham Edgecombe
quelle
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.

Maurice Perry
quelle
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.

Andrei Savu
quelle
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.

Andy Johnson
quelle
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;
    }
}
Serhii Bohutskyi
quelle
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
11

Das Eclipse SDK enthält eine Klasse SocketUtil , die genau das tut, was Sie wollen. Sie können einen Blick in den Git- Quellcode werfen .

Jopa
quelle
2
Wenn sie sich Eclipse's Code
ansehen
6

Dies funktioniert bei mir unter Java 6

    ServerSocket serverSocket = new ServerSocket(0);
    System.out.println("listening on port " + serverSocket.getLocalPort());
Peter Lawrey
quelle
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();
Alex Panov
quelle
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));
}
OscarRyz
quelle
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();
oberlies
quelle
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 .

Extraneon
quelle
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.

Gerardo Roza
quelle
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 .

Hari Krishna
quelle
0

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

Cezar Terzian
quelle