Как найти доступный порт?


195

Я хочу запустить сервер, который слушает порт. Я могу указать порт явно, и это работает. Но я бы хотел найти порт автоматически. В связи с этим у меня два вопроса.

  1. В каком диапазоне номеров портов я должен искать? (Я использовал порты 12345, 12346 и 12347, и это было нормально).

  2. Как я могу узнать, не занят ли данный порт другим программным обеспечением?

Ответы:


281

Если вы не возражаете против используемого порта, укажите порт 0 для конструктора ServerSocket, и он будет прослушивать любой свободный порт.

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 хочет номер сокета, а не уже открытый сокет сервера. Так что это зависит.
Ворбургер

4
@vorburger Разумные API примут ноль в качестве действительного номера порта для прослушивания, а затем сообщат вам фактический прослушиваемый порт. Однако, есть не так много разумных API: многие программы специально проверяют прохождение 0 порта и отказываются от него (« ssh -D 127.0.0.0:0 ...Нет!»), Что действительно расстраивает. Нам пришлось исправлять целый ряд библиотек / программ, чтобы сделать их полезными для нас.
Joker_vD

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

57

Если вы передадите 0 в качестве номера порта конструктору ServerSocket, он выделит вам порт.


Да, я думаю, что это самое элегантное решение, но по некоторым причинам оно не работает в моем случае. Но мне все еще нужно выяснить, что именно не так.
Роман

1
@Roman: Почему это не работает? Обновите свой вопрос, чтобы включить это (или люди будут продолжать предлагать его) и объяснить, почему это решение не помогает вам.
FrustratedWithFormsDesigner,

3
Как мне найти этот порт от клиента? ;)
ред. 22

34

Начиная с Java 1.7 вы можете использовать try-with-resources, например так:

  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. Поскольку Википедия не всегда является источником абсолютной правды / фактов, я подумал, что было бы полезно отметить, что ссылка на эту страницу Википедии взята из "Органа по присвоению номеров в Интернете (IANA)" [Имя службы и номер порта транспортного протокола Реестр ( страница 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;
    }
}

было интересно, как проверить порт, а затем отвязать от него. Спасибо SergeyB!
Влад Илие

5
Если используется каждый порт в диапазоне [от, до), этот код будет зацикливаться бесконечно (или, по крайней мере, до тех пор, пока один из этих портов не станет свободным). Если вы выполняете последовательное сканирование, а не выбираете порты в диапазоне случайным образом, вы можете избежать этой возможности (просто выведите исключение, когда вы доберетесь до конца диапазона, не найдя свободный порт). Если вам действительно нужно выбирать порты случайным образом, тогда вам нужен набор для отслеживания портов, которые вы пробовали до сих пор, а затем выдает ошибку, когда размер этого набора равен - from.
Саймон Киссейн

Порт 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).
Алексей Березкин

11

Eclipse SDK содержит класс SocketUtil , который делает то, что вы хотите. Вы можете заглянуть в исходный код git .


2
Глядя на код Eclipse, они делают то же самое, что и ответ Грэма Эджкомба
KevinL


6

Это работает для меня на Java 6

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

6

Недавно я выпустил крошечную библиотеку для этого, имея в виду тесты. Maven зависимость это:

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

После установки номера свободных портов можно получить через:

int port = FreePortFinder.findFreeLocalPort();

4

Если ваш сервер запускается, то этот сокет не использовался.

РЕДАКТИРОВАТЬ

Что-то вроде:

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) у меня есть файл / etc / services, в котором указаны по крайней мере порты, используемые / зарезервированные некоторыми приложениями. Это стандартные порты для этих приложений.

Нет никаких гарантий, что они работают, только порты по умолчанию, которые используют эти приложения (поэтому вы не должны использовать их, если это возможно).

Определено чуть более 500 портов, около половины UDP и половина TCP.

Файлы создаются с использованием информации IANA, см. Назначенные номера портов IANA .


есть аналогичный (и более полный, IIRC) список, который входит в состав nmap. +1
rmeador

3

Если вы используете Spring, ответ, предоставленный Михаилом Николаевым, является самым простым и чистым, и IMO следует проголосовать без голосования. Просто для удобства я добавлю встроенный пример с использованием метода Springframwework SocketUtils .findAvailableTcpPort () :

int randomAvailablePort = SocketUtils.findAvailableTcpPort();

Это так просто, всего одна строчка :). Конечно, класс Utils предлагает много других интересных методов, я предлагаю взглянуть на документы.


1

Используя класс «ServerSocket», мы можем определить, используется ли данный порт или свободен. ServerSocket предоставляет конструктор, который принимает целое число (то есть номер порта) в качестве аргумента и инициализирует сокет сервера на порту. Если ServerSocket генерирует какое-либо исключение ввода-вывода, мы можем предположить, что этот порт уже используется.

Следующий фрагмент используется для получения всех доступных портов.

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.