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




Если вас не слишком заботит производительность, вы всегда можете попробовать прослушивать порт с помощью класса 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 ();}
Вы также можете установить "portTaken = (socket == null);" в конце вместо того, чтобы делать это в улове.
Это пытается привязаться к сокету на всех (0.0.0.0) интерфейсах. Вы можете использовать явные адреса для проверки определенных портов на определенных адресах. см. java.sun.com/j2se/1.4.2/docs/api/java/net/…
Я не чувствовал, что это попадает в рамки заданного автором вопроса.
Небольшая ошибка в том, что инициализация должна быть: ServerSocket serverSocket = null;
Есть ли способ сделать это без try / catch? Я бы не прочь использовать доступный порт любой для своих целей, но перебирать номера портов и пробовать / перебирать каждый, пока не найду свободный, немного расточительно. Нет ли где-нибудь метода 'native boolean available (int)', который просто проверяет?
new SeverSocket (0) автоматически выберет свободный порт.
У меня то же требование, что и у Оз. Тем не менее, ServerSocket (0) звучит как второй лучший, поэтому я проголосовал за оба этих комментария. Я думаю, что ServerSocket (0) действительно стоит поставить как отдельный полный ответ.
@HughPerkins Это намного лучше первый-best.
Это выполнение из проекта 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), который дает вам следующий непривилегированный порт.
Однако это не улавливает все. Меня укусил работающий nginx на TCP 8000, в то время как вышеупомянутая процедура сообщает, что 8000 доступно. Понятия не имею, почему - я подозреваю, что nginx делает некоторые подлые вещи (это на OS X). Для меня обходной путь - это сделать описанное выше, а также открыть новый Socket ("localhost", порт), а затем вернуть false, если мы не получим исключения. Мысли?
У меня была такая же проблема с OS X.
Та же проблема в Windows при попытке определить, работает ли SMTP-сервер papercut. Решение, в котором вы открываете соединение с портом, а не пытаетесь привязаться к нему (как было предложено комментарием Partly Clodys и ответом Ивана), кажется, работает более надежно.
Интересно. Вызов setReuseAddress () совершенно бессмысленен после того, как сокет уже привязан, и это также полностью противоречит цели кода.
без setReuseAddress порт не мог бы использоваться в течение некоторого времени после закрытия сокета. На самом деле это системный вызов, который вызывается после привязки сокета
@pendrive На самом деле это системный вызов, который должен называться перед, связывающий сокет. Звонить потом - пустая трата времени.
Этот код подвержен проблемам с временным окном. Если цель состоит в том, чтобы создать ServerSocket, слушающий какой-либо порт, он должен это сделать и вернуть ServerSocket. Создание его, а затем его закрытие и возврат дает нулевую гарантию того, что последующий ServerSocket может быть создан с использованием этого порта. То же для DatagramSocket.
В моем случае это помогло попытаться подключиться к порту - если служба уже присутствует, она ответит.
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);
}
Не то, о чем просили.
Похоже, что с 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);
}
}
}
}
Я хочу проверить, доступен ли прокси-сервер для звонков от его имени.
Ответ @ DavidSantamaria никогда не работал. Ничего общего с Java 7.
Для 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-адрес и т. д.
Кроме того, этот тест довольно медленный (почти секунда на порт).
не должен ли он возвращать false при обнаружении исключения IOException?
@BaroudiSafwen Это полностью зависит от фактического исключения. Для ConnectException: 'connection refused' да, он должен вернуть false. Для тайм-аутов ничего, что он мог бы вернуть, не будет действительным, поскольку фактический ответ неизвестен. Вот почему этот метод бесполезен для этой цели.
Решения на основе сокетов 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 Я думаю, у вас все еще есть контроль над пакетом развертывания приложения, если это так, вы всегда можете предоставить встроенные библиотеки DLL / SO Sigar рядом с вашими JAR и прагматично установить путь к библиотеке JAVA (с помощью простого взлома вы можете повлиять на него даже после приложение было запущено JVM).
Я пробовал что-то подобное, и у меня это отлично сработало
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");
}
Не то, о чем просили.
А также утечка сокета.
В моем случае мне пришлось использовать класс 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-портов.
Следующее решение основано на реализации 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 и вернется).
Хотя этот вопрос касается того, как узнать, используется ли уже данный порт, вы, возможно, приземлились здесь, пытаясь найти способ получить бесплатный номер порта, который stackoverflow.com/questions/2675362/… (со ссылкой на мой gist.github.com/3429822) покрывает лучше.