Розетки: Дізнайтеся про наявність порту за допомогою 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.