Як знайти доступний порт?


195

Я хочу запустити сервер, який слухає порт. Я можу точно вказати порт, і він працює. Але я хотів би знайти порт автоматичним способом. У цьому відношенні у мене є два питання.

  1. У якому діапазоні номерів портів слід шукати? (Я використовував порти 12345, 12346 та 12347, і це було чудово).

  2. Як я можу дізнатися, якщо даний порт не зайнятий іншим програмним забезпеченням?

Відповіді:


280

Якщо ви не заперечуєте використовуваний порт, вкажіть порт конструктору ServerSocket 0 на 0, і він прослухає будь-який вільний порт.

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

Якщо ви хочете використовувати певний набір портів, то найпростіший спосіб - це, мабуть, повторити їх, поки один не працює. Щось на зразок цього:

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

Можна використовувати так:

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
Під час використання нового ServerSocket (0) слід подбати про його закриття! На основі javasourcecode.org/html/open-source/eclipse/eclipse-3.5.2/org/… , трохи адаптований у моєму gist.github.com/3429822
vorburger

9
@vorburger, робивши це таким чином, схильний до перегонів. Приємніше просто одразу прослухати серверний сокет, а не відкривати його, щоб знайти вільний порт, закрити його ще раз, а потім знову відкрити його на вільному порту (до цього часу є невеликий шанс, що щось інше зараз слухає на цьому порт.)
Грехем Еджкомб

7
погоджено, але це залежить від конкретного випадку використання: у моєму випадку мені потрібно було знайти номер безкоштовного порту, щоб передати його в якийсь API (скажімо, вбудований Jetty стартер, для тестів) - відповідний API хоче номер сокета - не вже відкритий серверний сокет. Так це залежить.
vorburger

4
@vorburger Розумні API приймуть нуль як дійсний номер порту для прослуховування, а потім повідомлять вам про фактичний порт, який слухають. Ховавер, не дуже багато розумних API: багато програм спеціально перевіряють пропуск 0 порту і відмовляються від нього ( ssh -D 127.0.0.0:0 ...? Ні!), Що насправді засмучує. Нам довелося виправити чимало бібліотек / програм, щоб зробити їх корисними для нас.
Joker_vD

2
@vorburger Під час використання будь-якого порту слід подбати про його закриття. new ServerSocket(0)не відрізняється в цьому відношенні. У вашому першому посиланні нічого про це немає. Ваше друге посилання просто виявляє погану практику. Після побудови ServerSocketви повинні використовувати його, а не закривати його та намагатися повторно використовувати той же номер порту. Все це є марною тратою часу, а також є вразливим до проблем з вікном часу.
Маркіз Лорн

57

Якщо ви передасте 0 як номер порту конструктору ServerSocket, він виділить порт для вас.


Так, я думаю, що це найелегантніше рішення, але через деякі причини воно не працює в моєму випадку. Але мені ще потрібно з’ясувати, що саме не так.
Роман

1
@ Роман: Чому це не працює? Оновіть своє запитання, щоб включити це (або люди продовжуватимуть його пропонувати) та поясніть, чому це рішення не відповідає вам.
FrustratedWithFormsDesigner

2
Як знайти цей порт у клієнта? ;)
ed22

34

Починаючи з Java 1.7, ви можете використовувати пробні ресурси, як це:

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

    }
  }

Якщо вам потрібно знайти відкритий порт на певному інтерфейсі, перевірте документацію ServerSocket на альтернативні конструктори.

Попередження: Будь-який код, що використовує номер порту, повернутий цим методом, залежить від гоночної умови - інший процес / потік може прив'язуватися до того ж порту відразу після закриття екземпляра ServerSocket.


1
Це може не спрацювати, якщо ви не встановите SO_REUSEADDR. І є умова гонки, але це важко виправити.
Девід Ерман

Це може не спрацювати, якщо згодом ви спробуєте відкрити інший ServerSocket з тим же номером порту, як це, можливо, було зроблено тим часом. Чому б ви не просто повернули фактичне, ServerSocketа не закривали, залишається загадкою.
Маркіз Лорн

5
@EJP Іноді код третьої сторони приймає порт, але не сам сокет.
Людина капітана

@CaptainMan Звичайно, але в цих випадках порт, безумовно, вважається попередньо розподіленим. Динамічно розподілений порт повинен надаватись клієнтам за допомогою якогось іншого механізму, наприклад, послуги іменування, трансляції чи передачі багатолітніх програм. Все питання тут неправильне.
Маркіз Лорн

@ user207421 Ви не можете змінити код сторонніх розробників, щоб прийняти ServerSocketзамість intпорту.
Людина капітана

30

Згідно з Вікіпедією , ви повинні використовувати порти 49152для65535 якщо не потрібен «добре відомих» порту.

AFAIK - єдиний спосіб визначити, чи використовується порт, це спробувати відкрити його.


6
+1. Оскільки Вікіпедія не завжди є джерелом абсолютної істини / фактів, я вважаю, що може бути корисним зазначити, що посилання на цю сторінку Вікіпедії походить від "s" [Ім'я послуги та транспортний протокол порта " Реєстр ( сторінка iana.org/assignments/service-names-port-numbers/… ", заснована на RFC 6335 - Розділ 6 (тобто" Суцільне "посилання в цьому випадку! :)).
OzgurH

25

Якщо вам потрібно в дальньому використанні:

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

цікавилося, як перевірити порт, а потім від’єднати від нього. Дякую СергіюБ!
Влад Ілі

5
Якщо використовується кожен порт у діапазоні [від, до], цей код буде циклічно нескінченним (або принаймні, поки один з цих портів не стане вільним). Якщо ви робите послідовне сканування, а не збираєте порти у діапазоні навмання, ви можете уникнути цієї можливості (просто киньте виняток, коли дістанетесь до кінця діапазону, не знайшовши вільного порту). Якщо вам дійсно потрібно вибрати порти випадковим чином, тоді вам потрібен набір для відстеження портів, які ви намагалися до цього часу, а потім викликати помилку, коли розмір цього набору дорівнює - від.
Саймон Кіссане

Порт 0 значно простіший і не потребує створення двох ServerSocketsу випадку успіху, і не страждає від проблем із тимчасовим вікном цього коду.
Маркіз Лорн

21

Якщо ви використовуєте Spring, ви можете спробувати http://docs.spring.io/spring/docs/4.0.5.RELEASE/javadoc-api/org/springframework/util/SocketUtils.html#findAvailableTcpPort--


Для тих, хто цікавиться, під кришкою SpringUtils.findAvailableTcpPort()робиться саме те, що рекомендується в інших відповідях: виберіть порт, а потім спробуйте new ServerSocket(port).
Олексій Березкін



6

Це працює для мене на Java 6

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

6

Нещодавно я випустив крихітну бібліотеку для того, щоб робити саме це з тестами на увазі. Залежна залежність:

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

Після встановлення безкоштовні номери портів можна отримати за допомогою:

int port = FreePortFinder.findFreeLocalPort();

4

Якщо ваш сервер запускається, цей сокет не використовувався.

EDIT

Щось на зразок:

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

Не знаю, хто вас проголосував, але я проголосував за вас. Ви можете налаштувати рекурсивну функцію для спроб налаштування ServerSocket, і якщо ви отримаєте IOException (або все, що це таке), ви спробуйте ще раз, поки він успішно не отримає порт.
jonescb

Я думаю, що краще перевірити, чи є порт, а потім спробувати почати слухати цей порт. Не здається елегантним щось почати, а потім з’ясувати, що є якась проблема, а потім спробувати вирішити ці проблеми.
Роман

2
@ Роман добре, так, це було б краще, за винятком того, що немає способу дізнатися, чи є порт. Якщо new ServerSocket(0)не працює для вашої альтернативи, це це. Я думаю, що є 85% можливостей, коли ти, нарешті, використовуєш мою пропозицію.
OscarRyz

Цей код постійно відкривається та протікає ServerSocketsназавжди і зберігає лише останній, який вдався. Йому потрібна breakзаява.
Маркіз Лорн

4

Якщо ви хочете створити власний сервер за допомогою ServerSocket, ви можете просто запропонувати йому вибрати безкоштовний порт для вас:

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

Інші реалізації сервера зазвичай мають подібну підтримку. Наприклад Jetty вибирає вільний порт, якщо ви прямо не встановите його:

  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

Це може вам не дуже допомогти, але на моїй машині (Ubuntu) у мене є файл / тощо / служби, в яких принаймні надаються порти, використовувані / зарезервовані деякими програмами. Це стандартні порти для цих додатків.

Ніяких гарантій того, що вони запущені, використовуються лише порти за замовчуванням, які ці програми використовують (тому не слід використовувати їх, якщо можливо).

Визначено трохи більше 500 портів, приблизно половина UDP і половина TCP.

Файли створюються за допомогою інформації IANA, див . Номери портових присвоєних IANA .


існує аналогічний (і більш повний список) IIRC, який входить до складу nmap. +1
rmeador

3

Якщо ви користуєтесь Весною, відповідь, яку надав Михайло Ніколаєв, є найпростішою та найчистішою, і ІМО слід схвалювати. Для зручності я додам вбудований приклад за допомогою методу Springframwework SocketUtils .findAvailableTcpPort () :

int randomAvailablePort = SocketUtils.findAvailableTcpPort();

Так просто, лише один рядок :). Звичайно, клас Utils пропонує багато інших цікавих методів, я пропоную ознайомитися з документами.


1

Використовуючи клас «ServerSocket», ми можемо визначити, чи використовується даний порт чи вільний. ServerSocket надає конструктор, який приймає ціле число (що є номером порту) як аргумент та ініціалізує серверний сокет на порту. Якщо ServerSocket викидає будь-який виняток IO, то можна припустити, що цей порт вже використовується.

Наступний фрагмент використовується для отримання всіх доступних портів.

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

         }
}

Посилання на посилання .


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.