Сокеты: обнаружение доступности порта с помощью Java

Как программно определить доступность порта на данном компьютере с помощью Java?

т.е. учитывая номер порта, определить, используется ли он уже или нет ?.

Хотя этот вопрос касается того, как узнать, используется ли уже данный порт, вы, возможно, приземлились здесь, пытаясь найти способ получить бесплатный номер порта, который stackoverflow.com/questions/2675362/… (со ссылкой на мой gist.github.com/3429822) покрывает лучше.

vorburger 23.08.2012 02:12

С какой целью? Если вы просто хотите найти свободный сокет для прослушивания, просто укажите ноль. Любая техника сканирования подвержена проблемам с временным окном: порт может быть пустым, когда вы сканируете, и занятым, когда вы подаете заявку.

user207421 28.02.2016 22:38
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
127
2
148 223
10

Ответы 10

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

public static boolean isAvailable(int portNr) {
  boolean portFree;
  try (var ignored = new ServerSocket(portNr)) {
      portFree = true;
  } catch (IOException e) {
      portFree = false;
  }
  return portFree;
}

Обновлено: Если все, что вы пытаетесь сделать, это выбрать свободный порт, new ServerSocket(0) найдет его для вас.

Используйте finally для очистки (попробуйте {...} catch {...} finally {if (socket! = Null) socket.close ();}

Hosam Aly 12.01.2009 10:51

Вы также можете установить "portTaken = (socket == null);" в конце вместо того, чтобы делать это в улове.

Hosam Aly 12.01.2009 10:52

Это пытается привязаться к сокету на всех (0.0.0.0) интерфейсах. Вы можете использовать явные адреса для проверки определенных портов на определенных адресах. см. java.sun.com/j2se/1.4.2/docs/api/java/net/…

cletus 12.01.2009 11:32

Я не чувствовал, что это попадает в рамки заданного автором вопроса.

Spencer Ruport 12.01.2009 11:35

Небольшая ошибка в том, что инициализация должна быть: ServerSocket serverSocket = null;

royalghost 17.04.2009 02:04

Есть ли способ сделать это без try / catch? Я бы не прочь использовать доступный порт любой для своих целей, но перебирать номера портов и пробовать / перебирать каждый, пока не найду свободный, немного расточительно. Нет ли где-нибудь метода 'native boolean available (int)', который просто проверяет?

Oren Shalev 21.05.2009 16:41

new SeverSocket (0) автоматически выберет свободный порт.

Spencer Ruport 21.05.2009 20:32

У меня то же требование, что и у Оз. Тем не менее, ServerSocket (0) звучит как второй лучший, поэтому я проголосовал за оба этих комментария. Я думаю, что ServerSocket (0) действительно стоит поставить как отдельный полный ответ.

Hugh Perkins 09.04.2011 05:16

@HughPerkins Это намного лучше первый-best.

user207421 25.11.2019 14:16

Это выполнение из проекта Apache верблюд:

/**
 * Checks to see if a specific port is available.
 *
 * @param port the port to check for availability
 */
public static boolean available(int port) {
    if (port < MIN_PORT_NUMBER || port > MAX_PORT_NUMBER) {
        throw new IllegalArgumentException("Invalid start port: " + port);
    }

    ServerSocket ss = null;
    DatagramSocket ds = null;
    try {
        ss = new ServerSocket(port);
        ss.setReuseAddress(true);
        ds = new DatagramSocket(port);
        ds.setReuseAddress(true);
        return true;
    } catch (IOException e) {
    } finally {
        if (ds != null) {
            ds.close();
        }

        if (ss != null) {
            try {
                ss.close();
            } catch (IOException e) {
                /* should not be thrown */
            }
        }
    }

    return false;
}

Они также проверяют DatagramSocket, чтобы проверить, доступен ли порт в UDP и TCP.

Надеюсь это поможет.

Хорошим вариантом этого, если у вас есть MINA, является AvailablePortFinder.getNextAvailable (1024), который дает вам следующий непривилегированный порт.

Alain O'Dea 27.06.2011 22:24

Однако это не улавливает все. Меня укусил работающий nginx на TCP 8000, в то время как вышеупомянутая процедура сообщает, что 8000 доступно. Понятия не имею, почему - я подозреваю, что nginx делает некоторые подлые вещи (это на OS X). Для меня обходной путь - это сделать описанное выше, а также открыть новый Socket ("localhost", порт), а затем вернуть false, если мы не получим исключения. Мысли?

Partly Cloudy 17.01.2012 02:59

У меня была такая же проблема с OS X.

OmerGertel 28.02.2012 23:59

Та же проблема в Windows при попытке определить, работает ли SMTP-сервер papercut. Решение, в котором вы открываете соединение с портом, а не пытаетесь привязаться к нему (как было предложено комментарием Partly Clodys и ответом Ивана), кажется, работает более надежно.

stian 06.12.2012 16:46

Интересно. Вызов setReuseAddress () совершенно бессмысленен после того, как сокет уже привязан, и это также полностью противоречит цели кода.

user207421 31.03.2013 03:59

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

madz 15.03.2015 01:58

@pendrive На самом деле это системный вызов, который должен называться перед, связывающий сокет. Звонить потом - пустая трата времени.

user207421 01.12.2015 11:40

Этот код подвержен проблемам с временным окном. Если цель состоит в том, чтобы создать ServerSocket, слушающий какой-либо порт, он должен это сделать и вернуть ServerSocket. Создание его, а затем его закрытие и возврат дает нулевую гарантию того, что последующий ServerSocket может быть создан с использованием этого порта. То же для DatagramSocket.

user207421 01.12.2015 11:41

В моем случае это помогло попытаться подключиться к порту - если служба уже присутствует, она ответит.

    try {
        log.debug("{}: Checking if port open by trying to connect as a client", portNumber);
        Socket sock = new Socket("localhost", portNumber);          
        sock.close();
        log.debug("{}: Someone responding on port - seems not open", portNumber);
        return false;
    } catch (Exception e) {         
        if (e.getMessage().contains("refused")) {
            return true;
    }
        log.error("Troubles checking if port is open", e);
        throw new RuntimeException(e);              
    }

Не то, о чем просили.

user207421 28.07.2015 01:55

Похоже, что с Java 7 Ответ Дэвида Сантамарии больше не работает надежно. Однако похоже, что вы все еще можете надежно использовать Socket для проверки соединения.

private static boolean available(int port) {
    System.out.println("--------------Testing port " + port);
    Socket s = null;
    try {
        s = new Socket("localhost", port);

        // If the code makes it this far without an exception it means
        // something is using the port and has responded.
        System.out.println("--------------Port " + port + " is not available");
        return false;
    } catch (IOException e) {
        System.out.println("--------------Port " + port + " is available");
        return true;
    } finally {
        if ( s != null){
            try {
                s.close();
            } catch (IOException e) {
                throw new RuntimeException("You should handle this error." , e);
            }
        }
    }
}

Я хочу проверить, доступен ли прокси-сервер для звонков от его имени.

Dejell 01.01.2015 19:34

Ответ @ DavidSantamaria никогда не работал. Ничего общего с Java 7.

user207421 01.12.2015 11:43

Для Java 7 вы можете использовать try-with-resource для более компактного кода:

private static boolean available(int port) {
    try (Socket ignored = new Socket("localhost", port)) {
        return false;
    } catch (IOException ignored) {
        return true;
    }
}

Это не проверяет, доступен ли порт. Он проверяет, находится ли он в состоянии LISTEN, доступен ли IP-адрес и т. д.

user207421 28.07.2015 01:55

Кроме того, этот тест довольно медленный (почти секунда на порт).

JMax 16.02.2018 17:23

не должен ли он возвращать false при обнаружении исключения IOException?

Baroudi Safwen 06.08.2019 12:49

@BaroudiSafwen Это полностью зависит от фактического исключения. Для ConnectException: 'connection refused' да, он должен вернуть false. Для тайм-аутов ничего, что он мог бы вернуть, не будет действительным, поскольку фактический ответ неизвестен. Вот почему этот метод бесполезен для этой цели.

user207421 25.11.2019 14:12

Решения на основе сокетов try / catch могут не дать точных результатов (адрес сокета - «localhost», и в некоторых случаях порт может быть «занят» не интерфейсом обратной петли, и, по крайней мере, в Windows, я видел, что этот тест не прошел, т.е. Прот ложно объявлен доступным).

Есть классная библиотека под названием СИГАРА, следующий код может вас зацепить:

Sigar sigar = new Sigar();
int flags = NetFlags.CONN_TCP | NetFlags.CONN_SERVER | NetFlags.CONN_CLIENT;             NetConnection[] netConnectionList = sigar.getNetConnectionList(flags);
for (NetConnection netConnection : netConnectionList) {
   if ( netConnection.getLocalPort() == port )
        return false;
}
return true;

Я получаю: «no sigar-x86-winnt.dll in java.library.path» К сожалению, для этого требуется загрузка некоторой дополнительной dll, которая не является жизнеспособной в корпоративной среде (я не контролирую систему CI). Так плохо .... Все равно спасибо.

uthomas 18.04.2013 18:02

@uthomas Я думаю, у вас все еще есть контроль над пакетом развертывания приложения, если это так, вы всегда можете предоставить встроенные библиотеки DLL / SO Sigar рядом с вашими JAR и прагматично установить путь к библиотеке JAVA (с помощью простого взлома вы можете повлиять на него даже после приложение было запущено JVM).

Shmil The Cat 19.04.2013 14:04

Я пробовал что-то подобное, и у меня это отлично сработало

            Socket Skt;
            String host = "localhost";
            int i = 8983; // port no.

                 try {
                    System.out.println("Looking for "+ i);
                    Skt = new Socket(host, i);
                    System.out.println("There is a Server on port "
                    + i + " of " + host);
                 }
                 catch (UnknownHostException e) {
                    System.out.println("Exception occured"+ e);

                 }
                 catch (IOException e) {
                     System.out.println("port is not used");

                 }

Не то, о чем просили.

user207421 28.02.2016 22:40

А также утечка сокета.

user207421 01.02.2018 04:59

В моем случае мне пришлось использовать класс DatagramSocket.

boolean isPortOccupied(int port) {
    DatagramSocket sock = null;
    try {
        sock = new DatagramSocket(port);
        sock.close();
        return false;
    } catch (BindException ignored) {
        return true;
    } catch (SocketException ex) {
        System.out.println(ex);
        return true;
    }
}

Не забудьте сначала импортировать

import java.net.DatagramSocket;
import java.net.BindException;
import java.net.SocketException;

Это работает для портов UDP. Вопрос, похоже, касается TCP-портов.

user207421 01.02.2018 05:00

Следующее решение основано на реализации Spring-core SocketUtils (лицензия Apache).

По сравнению с другими решениями, использующими Socket(...), это довольно быстро (тестирование 1000 портов TCP менее чем за секунду):

public static boolean isTcpPortAvailable(int port) {
    try (ServerSocket serverSocket = new ServerSocket()) {
        // setReuseAddress(false) is required only on OSX, 
        // otherwise the code will not work correctly on that platform          
        serverSocket.setReuseAddress(false);
        serverSocket.bind(new InetSocketAddress(InetAddress.getByName("localhost"), port), 1);
        return true;
    } catch (Exception ex) {
        return false;
    }
}       

Очистка ответа, указанного Дэвидом Сантамария:

/**
 * Check to see if a port is available.
 *
 * @param port
 *            the port to check for availability.
 */
public static boolean isPortAvailable(int port) {
    try (var ss = new ServerSocket(port); var ds = new DatagramSocket(port)) {
        return true;
    } catch (IOException e) {
        return false;
    }
}

Это все еще зависит от состояния гонки, указанного пользователем 207421 в комментариях к ответу Дэвида Сантамария (что-то может захватить порт после того, как этот метод закроет ServerSocket и DatagramSocket и вернется).

Другие вопросы по теме