Jak znaleźć dostępny port?

194

Chcę uruchomić serwer, który nasłuchuje na porcie. Mogę wyraźnie określić port i działa. Ale chciałbym znaleźć port w sposób automatyczny. W związku z tym mam dwa pytania.

  1. W jakim zakresie numerów portów powinienem szukać? (Użyłem portów 12345, 12346 i 12347 i było dobrze).

  2. Jak mogę się dowiedzieć, czy dany port nie jest zajęty przez inne oprogramowanie?

rzymski
źródło

Odpowiedzi:

277

Jeśli nie przeszkadza ci użyty port, podaj port 0 dla konstruktora ServerSocket, a nasłuchi on na dowolnym wolnym porcie.

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

Jeśli chcesz użyć określonego zestawu portów, to najłatwiejszym sposobem jest iteracja przez nie, dopóki nie zadziała. Coś takiego:

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

Może być używany w następujący sposób:

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
źródło
2
Korzystając z nowego ServerSocket (0), należy zachować ostrożność, aby go zamknąć! Na podstawie javasourcecode.org/html/open-source/eclipse/eclipse-3.5.2/org/… , nieco dostosowany w mojej gist.github.com/3429822
vorburger
9
@vorburger, robienie tego w ten sposób jest podatne na warunki wyścigowe. Lepiej jest po prostu natychmiast nasłuchiwać na gnieździe serwera, niż otwierać go w celu znalezienia wolnego portu, zamknąć go ponownie, a następnie otworzyć ponownie na wolnym porcie (do tego czasu istnieje mała szansa, że ​​coś innego nasłuchuje na tym port.)
Graham Edgecombe
7
Zgadzam się, ale zależy to od dokładnego przypadku użycia: W moim przypadku musiałem znaleźć wolny numer portu, aby przekazać go do jakiegoś API (powiedzmy, wbudowany starter Jetty, do testów) - odpowiedni API chce numeru gniazda - nie jest to już otwarte gniazdo serwera. Więc to zależy.
vorburger
4
Rozsądne interfejsy API @vorburger zaakceptują zero jako prawidłowy numer portu do nasłuchiwania, a następnie podadzą rzeczywisty port, na którym nasłuchuje. Jednak nie ma wielu rozsądnych interfejsów API: wiele programów specjalnie testuje, czy port 0 jest przekazywany i odrzuca go ( ssh -D 127.0.0.0:0 ...? Nie!), Co jest naprawdę frustrujące. Musieliśmy załatać wiele bibliotek / programów, aby były dla nas przydatne.
Joker_vD
2
@vorburger Podczas korzystania z dowolnego portu należy zachować ostrożność, aby go zamknąć. new ServerSocket(0)nie różni się pod tym względem. W twoim pierwszym linku nie ma nic na ten temat. Twój drugi link pokazuje jedynie złą praktykę. Po zbudowaniu ServerSocketpowinieneś go użyć , nie zamykaj go i spróbuj ponownie użyć tego samego numeru portu. Wszystko to jest całkowitą stratą czasu, a także podatnością na problemy z oknem czasowym.
Markiz Lorne
57

Jeśli przekażesz 0 jako numer portu konstruktorowi ServerSocket, alokuje on port dla ciebie.

Maurice Perry
źródło
Tak, myślę, że jest to najbardziej eleganckie rozwiązanie, ale z pewnych powodów nie działa w moim przypadku. Ale wciąż muszę dowiedzieć się, co dokładnie jest nie tak.
Rzym.
1
@Roman: Dlaczego to nie działa? Zaktualizuj swoje pytanie, aby uwzględnić to (lub ludzie będą go sugerować) i wyjaśnij, dlaczego to rozwiązanie zawiedzie dla Ciebie.
FrustratedWithFormsDesigner
2
Jak znaleźć ten port od klienta? ;)
ed22
34

Począwszy od wersji Java 1.7 możesz używać try-with-resources w następujący sposób:

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

    }
  }

Jeśli potrzebujesz znaleźć otwarty port na określonym interfejsie, sprawdź dokumentację ServerSocket dla alternatywnych konstruktorów.

Ostrzeżenie: każdy kod korzystający z numeru portu zwróconego tą metodą podlega warunkom wyścigu - inny proces / wątek może zostać powiązany z tym samym portem natychmiast po zamknięciu instancji ServerSocket.

Andrei Savu
źródło
1
To może nie działać, jeśli nie ustawisz SO_REUSEADDR. I jest warunek wyścigu, ale trudno to naprawić.
David Ehrmann
Może to nie zadziałać, jeśli następnie spróbujesz otworzyć inny ServerSocket z tym samym numerem portu, ponieważ w międzyczasie mógł zostać pobrany. Dlaczego nie chcesz zwrócić rzeczywistego ServerSocketzamiast go zamknąć, pozostaje tajemnicą.
Markiz Lorne
5
@EJP Czasami kod innej firmy akceptuje port, ale nie samo gniazdo.
Kapitan Man
@CaptainMan Pewnie, ale w tych przypadkach zakłada się, że port jest wstępnie przydzielony. Dynamicznie przydzielany port musi być dostarczany klientom przez inny mechanizm, taki jak usługa nazewnictwa, emisja lub mulitcast. Całe pytanie tutaj jest zniekształcone.
Markiz Lorne
@ user207421 Nie można jednak zmienić kodu innej firmy, aby akceptował ServerSocketzamiast intportu dla portu.
Captain Man,
30

Według Wikipedii , należy użyć portów 49152do 65535jeśli nie potrzebujemy „znanego” port.

AFAIK jedynym sposobem ustalenia, czy port jest używany, jest próba jego otwarcia.

Andy Johnson
źródło
6
+1. Ponieważ Wikipedia nie zawsze jest źródłem absolutnej prawdy / faktów, pomyślałem, że warto zauważyć, że odniesienie do tej strony Wikipedii pochodzi z „Internet Assigned Numbers Authority (IANA)” [Nazwa usługi i numer portu protokołu transportu Rejestr ( strona iana.org/assignments/service-names-port-numbers/… ”, na podstawie RFC 6335 - sekcja 6 (w tym przypadku odniesienie„ Solid ”! :)).
OzgurH
25

Jeśli potrzebujesz w zasięgu, użyj:

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
źródło
zastanawiał się, jak sprawdzić port, a następnie odłączyć od niego. Dzięki SergeyB!
Vlad Ilie,
5
Jeśli używany jest każdy port z zakresu [od, do), ten kod będzie się zapętlał w nieskończoność (lub przynajmniej dopóki jeden z tych portów nie stanie się wolny). Jeśli wykonujesz skanowanie sekwencyjne, a nie wybierasz losowo porty z zakresu, możesz uniknąć tej możliwości (po prostu wyrzuć wyjątek, gdy dotrzesz do końca zakresu bez znalezienia wolnego portu). Jeśli naprawdę musisz wybierać porty losowo, potrzebujesz zestawu do śledzenia portów, które próbowałeś do tej pory, a następnie zgłaszania błędu, gdy rozmiar tego zestawu jest równy - od.
Simon Kissane,
Port 0 jest znacznie prostszy i nie wymaga utworzenia dwóch ServerSocketsw przypadku sukcesu, i nie ma problemów z oknem czasowym tego kodu.
Markiz Lorne
11

Zestaw Eclipse SDK zawiera klasę SocketUtil , która robi to, co chcesz. Możesz zajrzeć do kodu źródłowego git .

jopa
źródło
2
Patrząc na kod Eclipse, robią to samo, co odpowiedź Grahama Edgecombe'a
KevinL
6

Działa to dla mnie w Javie 6

    ServerSocket serverSocket = new ServerSocket(0);
    System.out.println("listening on port " + serverSocket.getLocalPort());
Peter Lawrey
źródło
6

Niedawno wydałem niewielką bibliotekę, aby robić to właśnie z myślą o testach. Zależność Maven to:

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

Po zainstalowaniu bezpłatne numery portów można uzyskać poprzez:

int port = FreePortFinder.findFreeLocalPort();
Alex Panov
źródło
4

Jeśli serwer się uruchomi, to gniazdo nie zostało użyte.

EDYTOWAĆ

Coś jak:

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
źródło
Nie wiem, kto cię przegłosował, ale głosowałem z powrotem. Możesz ustawić funkcję rekurencyjną, aby próbowała skonfigurować ServerSocket, a jeśli otrzymasz wyjątek IOException (lub cokolwiek to jest), spróbuj ponownie, aż uda mu się uzyskać port.
jonescb
Myślę, że lepiej jest sprawdzić, czy port jest dostępny, a następnie spróbować rozpocząć nasłuchiwanie tego portu. Nie wydaje się eleganckie, aby coś zacząć, a następnie dowiedzieć się, że jest jakiś problem, a następnie spróbować rozwiązać te problemy.
Rzym.
2
@Roman, tak, tak, byłoby lepiej, z wyjątkiem faktu, że nie ma metody, aby sprawdzić, czy port jest dostępny. Jeśli new ServerSocket(0)nie działa dla ciebie alternatywa jest następująca. Wydaje mi się, że istnieje 85% możliwości, z których możesz skorzystać przy mojej sugestii.
OscarRyz
Ten kod otwiera się i wycieka na ServerSocketszawsze i zachowuje tylko ten ostatni , któremu się udało. Potrzebuje breakoświadczenia.
Markiz Lorne
4

Jeśli chcesz utworzyć własny serwer za pomocą a ServerSocket, możesz po prostu wybrać dla niego wolny port:

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

Inne implementacje serwerów zazwyczaj mają podobne wsparcie. Na przykład Jetty wybiera wolny port, chyba że wyraźnie go ustawisz:

  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
źródło
3

Może ci to niewiele pomóc, ale na moim komputerze (Ubuntu) mam plik / etc / services, w którym podane są przynajmniej porty używane / zastrzeżone przez niektóre aplikacje. Są to standardowe porty dla tych aplikacji.

Brak gwarancji, że są one uruchomione, tylko domyślne porty, z których korzystają te aplikacje (więc nie należy ich używać, jeśli to możliwe).

Zdefiniowano nieco ponad 500 portów, około połowy UDP i połowy TCP.

Pliki są tworzone przy użyciu informacji IANA, patrz IANA Przypisane numery portów .

extraneon
źródło
istnieje podobna (i bardziej kompletna, IIRC) lista, która jest częścią nmap. +1
rmeador,
3

Jeśli używasz Springa, odpowiedź udzielona przez Michaila Nikołajewa jest najprostsza i najczystsza, a IMO powinno zostać ocenione pozytywnie. Dla wygody dodam wbudowany przykład za pomocą metody Springframwework SocketUtils .findAvailableTcpPort () :

int randomAvailablePort = SocketUtils.findAvailableTcpPort();

To takie proste, tylko jedna linia :). Oczywiście klasa Utils oferuje wiele innych interesujących metod, sugeruję zajrzeć do dokumentacji.

Gerardo Roza
źródło
1

Za pomocą klasy „ServerSocket” możemy ustalić, czy dany port jest używany, czy wolny. ServerSocket udostępnia konstruktor, który przyjmuje liczbę całkowitą (czyli numer portu) jako argument i inicjuje gniazdo serwera na porcie. Jeśli ServerSocket zgłasza wyjątek IO, możemy założyć, że ten port jest już w użyciu.

Poniższy fragment kodu służy do uzyskania wszystkich dostępnych portów.

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

         }
}

Link referencyjny .

Hari Krishna
źródło
0

jeśli port jest zajęty przez inne oprogramowanie, kod wygeneruje wyjątek IOException

Cezar Terzian
źródło