Розетки: Дізнайтеся про наявність порту за допомогою Java


123

Як я можу програмно визначити наявність порту в даній машині за допомогою Java?

тобто заданий номер порту, визначте, він уже використовується чи ні ?.


2
Хоча це питання стосується того, як дізнатись, чи вже використовується даний порт, ви, можливо, приземлилися тут, намагаючись знайти спосіб отримання безкоштовного порту, який stackoverflow.com/questions/2675362/… (із посиланням на мою суть .github.com / 3429822 ) краще покриває.
vorburger

З якою метою? Якщо ви просто хочете знайти вільну розетку для прослуховування, просто вкажіть нуль. Будь-яка техніка сканування може спричинити проблеми з вікном часу: порт може бути вакантним при скануванні та зайнятим, коли ви збираєтеся заявити заявку.
Маркіз Лорн,

Відповіді:


91

Це реалізація від проекту верблюда 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.

Сподіваюся, це допомагає.


5
Хороший варіант цього, якщо у вас є MINA - це AvailablePortFinder.getNextAvailable (1024), який отримує наступний непривілейований порт.
Ален О'Дея,

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

2
Ця ж проблема в Windows, яка намагається виявити, чи працює сервер SMTP з паперовим принтером. Рішення, коли ви відкриваєте з'єднання з портом, а не намагаєтесь прив’язатись до нього (як це запропоновано коментарем Частково Clodys та відповіддю Івана), здається, працює більш надійним.
stian

4
Цікаво. Виклик до setReuseAddress () є абсолютно безглуздим після того, як сокет вже пов'язаний, і це також повністю суперечить призначенню коду.
Маркіз Лорн

3
Цей код є проблемою з тимчасовим вікном. Якщо мета полягає в тому, щоб створити ServerSocketпрослуховування якогось порту, це те, що він повинен робити, і повернути його ServerSocket. Створюючи його, а потім закриваючи та повертаючи, це дає нульову гарантію того, що наступне ServerSocketможна створити за допомогою цього порту. Дітто для DatagramSocket.
Маркіз Лорнський

41

Для Java 7 ви можете скористатися пробним ресурсом для більш компактного коду:

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

15
Це не перевіряє, чи доступний в порту. Він перевіряє, чи перебуває він у стані LISTEN, чи доступна IP-адреса тощо.
Маркіз Лорн

1
Крім того, цей тест є досить повільним (майже секунду на порт).
JMax

не повинен він повертати помилкове, коли спіймано IOException?
Baroudi Safwen

@BaroudiSafwen Це повністю залежить від того, що насправді є винятком. Бо ConnectException: 'connection refused'так, він повинен повернути помилкове. Для тайм-аутів нічого, що воно може повернути, було б справедливим, оскільки фактична відповідь не відома. Ось чому ця методика для цього марна.
Маркіз Лорн

36

Схоже, що на 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

1
@ Відповідь DavidSantamaria ніколи не працювала. Немає нічого спільного з Java 7.
Маркіз Лорн

35

Якщо ви не надто переймаєтесь продуктивністю, ви завжди можете спробувати прослуховування через порт, використовуючи клас 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)знайдете його.


Нарешті використовуйте для очищення (спробуйте {...} catch {...} нарешті {if (socket! = Null) socket.close ();}
Hosam Aly

Ви також можете встановити "portTaken = (socket == null);" врешті-решт замість того, щоб робити це в улові.
Хосам Алі

1
Я не відчував, що потрапляв у передбачуваний обсяг запитання автора.
Спенсер Рупорт

1
Чи є спосіб зробити це без спроби / лову? Я б не проти використовувати будь-який доступний порт для своїх цілей, але ітерація над номерами портів і спробування / перехоплення кожного з них до тих пір, поки я не знайду вільний - трохи марно. Чи не існує десь методу "нативної булі" (int), який просто перевіряє?
Орен Шалев

27
новий SeverSocket (0) автоматично вибере вільний порт.
Спенсер Рупорт

10

Наступне рішення натхнене реалізацією SocketUtils Spring-core (ліцензія 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;
    }
}       

3

Рішення на основі софт / захоплення можуть не дати точних результатів (адреса сокета - "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;

Я отримую: "no sigar-x86-winnt.dll in java.library.path" На жаль, для цього потрібно завантажити якийсь додатковий dll, який насправді не життєздатний у корпоративному середовищі (у мене немає контролю над системою CI). Так погано .... Спасибі все одно.
утом

@uthomas Я думаю, ви все ще маєте контроль над своїм пакетом для розгортання додатків, якщо це так, ви завжди можете надати сигаловий DLL / SO для власного часу виконання Sigar біля своїх JAR і встановити шлях JAVA lib прагматично (за допомогою простого злому ви можете впливати на нього навіть після вашого додаток запущено JVM).
Shmil The Cat

2

Відповідь, яку вказав Девід Сантамарія:

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

Це все ще є предметом умови гонки вказували на user207421 в коментарях до відповіді Девід Сантамарія ( в ніж - то може захопити порт після того, як цей метод закриває ServerSocketі DatagramSocketі повертається).


1

У моєму випадку це допомогло спробувати підключитися до порту - якщо служба вже присутня, вона відповість.

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

1
Не те, що просили.
Маркіз Лорн

0

У моєму випадку мені довелося використовувати клас 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.
Маркіз Лорн

-2

Я спробував щось подібне, і зі мною це спрацювало чудово

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

                 }

1
Не те, що просили.
Маркіз Лорн

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