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

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

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


person user54075    schedule 12.01.2009    source источник
comment
Хотя этот вопрос касается того, как узнать, используется ли уже данный порт, вы, возможно, приземлились здесь, пытаясь найти способ получить бесплатный номер порта, который stackoverflow.com/questions/2675362/ (со ссылкой на мой gist.github.com/3429822) лучше охватывает.   -  person vorburger    schedule 23.08.2012
comment
Для чего? Если вы просто хотите найти свободный сокет для прослушивания, просто укажите ноль. Любая техника сканирования подвержена проблемам с временным окном: порт может быть пустым, когда вы сканируете, и занятым, когда вы подаете заявку.   -  person user207421    schedule 28.02.2016


Ответы (10)


Это реализация из проекта Apache camel:

/**
 * 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.

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

person David Santamaria    schedule 12.01.2009
comment
Хорошим вариантом этого, если у вас есть MINA, является AvailablePortFinder.getNextAvailable (1024), который дает вам следующий непривилегированный порт. - person Alain O'Dea; 27.06.2011
comment
Однако это не улавливает все. Меня укусил работающий nginx на TCP 8000, в то время как вышеупомянутая процедура сообщает, что 8000 доступно. Понятия не имею, почему - я подозреваю, что nginx делает некоторые подлые вещи (это на OS X). Для меня обходной путь - это сделать выше, а также открыть новый Socket (localhost, port), а затем вернуть false, если мы не получим исключение. Мысли? - person Partly Cloudy; 17.01.2012
comment
У меня была такая же проблема с OS X. - person OmerGertel; 28.02.2012
comment
Та же проблема в Windows при попытке определить, работает ли SMTP-сервер papercut. Решение, в котором вы открываете соединение с портом, а не пытаетесь привязаться к нему (как было предложено комментарием Partly Clodys и ответом Ивана), кажется, работает более надежно. - person stian; 06.12.2012
comment
Интересно. Вызов setReuseAddress () совершенно бессмысленен после того, как сокет уже привязан, и это также полностью противоречит цели кода. - person user207421; 31.03.2013
comment
без setReuseAddress порт не мог бы использоваться в течение некоторого времени после закрытия сокета. На самом деле это системный вызов, который вызывается после привязки сокета - person madz; 15.03.2015
comment
@pendrive На самом деле это системный вызов, который следует вызывать перед привязкой сокета. Звонить потом - пустая трата времени. - person user207421; 01.12.2015
comment
Этот код подвержен проблемам с временным окном. Если цель состоит в том, чтобы создать ServerSocket, слушающий какой-либо порт, это то, что он должен сделать, и вернуть ServerSocket. Создание его, а затем его закрытие и возврат дает нулевую гарантию того, что последующий ServerSocket может быть создан с использованием этого порта. То же для DatagramSocket. - person user207421; 01.12.2015

Для 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;
    }
}
person eivindw    schedule 11.03.2013
comment
Это не проверяет, доступен ли порт. Он проверяет, находится ли он в состоянии LISTEN, доступен ли IP-адрес и т. Д. - person user207421; 28.07.2015
comment
Кроме того, этот тест довольно медленный (почти секунда на порт). - person JMax; 16.02.2018
comment
не должен ли он возвращать false при обнаружении исключения IOException? - person Baroudi Safwen; 06.08.2019
comment
@BaroudiSafwen Это полностью зависит от фактического исключения. Для ConnectException: 'connection refused' да, он должен вернуть false. Для тайм-аутов ничего, что он мог бы вернуть, не будет действительным, поскольку фактический ответ неизвестен. Поэтому этот прием для этой цели бесполезен. - person user207421; 25.11.2019

Похоже, что с 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);
            }
        }
    }
}
person TwentyMiles    schedule 11.12.2012
comment
Я хочу проверить, доступен ли прокси-сервер для звонков от его имени. - person Dejell; 01.01.2015
comment
Ответ @ DavidSantamaria никогда не работал. Ничего общего с Java 7. - person user207421; 01.12.2015

Если вас не слишком заботит производительность, вы всегда можете попробовать прослушивать порт, используя 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) найдет его для вас.

person Spencer Ruport    schedule 12.01.2009
comment
Используйте finally для очистки (попробуйте {...} catch {...} finally {if (socket! = Null) socket.close ();} - person Hosam Aly; 12.01.2009
comment
Вы также можете установить portTaken = (socket == null); в конце вместо того, чтобы делать это в улове. - person Hosam Aly; 12.01.2009
comment
Это пытается привязаться к сокету на всех (0.0.0.0) интерфейсах. Вы можете использовать явные адреса для проверки определенных портов на определенных адресах. см. java.sun.com/j2se/1.4.2/docs/api/java/net/ - person cletus; 12.01.2009
comment
Я не чувствовал, что это попадает в рамки заданного автором вопроса. - person Spencer Ruport; 12.01.2009
comment
Небольшая ошибка в том, что инициализация должна быть: ServerSocket serverSocket = null; - person royalghost; 17.04.2009
comment
Есть ли способ сделать это без try / catch? Я бы не прочь использовать любой доступный порт для своих целей, но перебирать номера портов и пробовать / перебирать каждый, пока не найду свободный, немного расточительно. Нет ли где-нибудь метода 'native boolean available (int)', который просто проверяет? - person Oren Shalev; 21.05.2009
comment
new SeverSocket (0) автоматически выберет свободный порт. - person Spencer Ruport; 21.05.2009
comment
У меня то же требование, что и у Оз. Однако ServerSocket (0) звучит как второй лучший, поэтому я проголосовал за оба этих комментария. Я думаю, что ServerSocket (0) действительно стоит поставить как отдельный полный ответ. - person Hugh Perkins; 09.04.2011
comment
@HughPerkins Это намного лучший первый -самый лучший. - person user207421; 25.11.2019

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

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

Конечно, он может обнаруживать только службы, которые открывают порт на всех интерфейсах или явно запускаются на localhost.

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;
    }
}       
person JMax    schedule 16.02.2018
comment
Спасибо, у меня работал на macOS 11 (Big Sur) - person AbstractVoid; 21.04.2021

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

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

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;
person Shmil The Cat    schedule 12.03.2013
comment
Я получаю: no sigar-x86-winnt.dll в java.library.path К сожалению, для этого требуется загрузка некоторой дополнительной dll, которая на самом деле не жизнеспособна в корпоративной среде (у меня нет контроля над системой CI). Так плохо .... Все равно спасибо. - person uthomas; 18.04.2013
comment
@uthomas Я думаю, у вас все еще есть контроль над пакетом развертывания приложения, если это так, вы всегда можете предоставить встроенные библиотеки DLL / SO Sigar рядом с вашими JAR и прагматично установить путь к JAVA lib (с помощью простого взлома вы можете повлиять на него даже после приложение было запущено JVM). - person Shmil The Cat; 19.04.2013

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

/**
 * 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 и вернется).

person Luke Hutchison    schedule 08.03.2019

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

    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);              
    }
person Ivan    schedule 31.05.2012
comment
Не то, о чем просили. - person user207421; 28.07.2015

В моем случае мне пришлось использовать класс 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;
person kmchmk    schedule 06.11.2017
comment
Это работает для портов UDP. Вопрос, похоже, касается TCP-портов. - person user207421; 01.02.2018

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

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

                 }
person mayu jadhv    schedule 28.10.2015
comment
Не то, о чем просили. - person user207421; 28.02.2016
comment
А также утечка сокета. - person user207421; 01.02.2018