Java: общий способ проверки и преобразования "host: port" в InetSocketAddress?
Каков общий способ в Java для проверки и преобразования строки формы host:port
в экземпляр InetSocketAddress
?
Было бы хорошо, если бы были выполнены следующие критерии:
-
Нет поиска адресов;
-
Работа с именами хостов IPv4, IPv6 и "string".
(Для IPv4 он ip:port
, для IPv6 он [ip]:port
, правильно? Есть ли какой-то RFC, который определяет все эти схемы?)
-
Предпочтительно без разбора строки вручную.
(Я думаю обо всех этих особых случаях, когда кто-то думает, что он знает все допустимые формы адресов сокетов, но забывает о "этом специальном случае", что приводит к неожиданным результатам.)
Ответы
Ответ 1
Я сам предлагаю одно возможное решение.
Преобразуйте строку в URI (это подтвердит ее автоматически), а затем запросите компоненты узла и порта URI.
К сожалению, URI с хост-компонентом ДОЛЖЕН иметь схему. Вот почему это решение "не идеально".
String string = ... // some string which has to be validated
try {
// WORKAROUND: add any scheme to make the resulting URI valid.
URI uri = new URI("my://" + string); // may throw URISyntaxException
String host = uri.getHost();
int port = uri.getPort();
if (uri.getHost() == null || uri.getPort() == -1) {
throw new URISyntaxException(uri.toString(),
"URI must have host and port parts");
}
// here, additional checks can be performed, such as
// presence of path, query, fragment, ...
// validation succeeded
return new InetSocketAddress (host, port);
} catch (URISyntaxException ex) {
// validation failed
}
Это решение не требует специального анализа строк, работает с IPv4 (1.1.1.1:123
), IPv6 ([::0]:123
) и именами хостов (my.host.com:123
).
Случайно, это решение хорошо подходит для моего сценария. В любом случае я собирался использовать схемы URI.
Ответ 2
Регулярное выражение будет делать это довольно аккуратно:
Pattern p = Pattern.compile("^\\s*(.*?):(\\d+)\\s*$");
Matcher m = p.matcher("127.0.0.1:8080");
if (m.matches()) {
String host = m.group(1);
int port = Integer.parseInt(m.group(2));
}
Это можно сделать многими способами, например, сделать порт опцией или выполнить некоторую проверку на хосте.
Ответ 3
Он точно не отвечает на вопрос, но этот ответ может быть полезен другим, как я, который просто хочет разбирать хост и порт, но не обязательно полный InetAddress
. У Guava есть класс HostAndPort с помощью метода parseString
.
Ответ 4
Другой человек дал регулярный ответ, который я делал, когда я задавал вопрос о хозяевах. Я все равно сделаю это, потому что это пример регулярного выражения, который немного продвинулся вперед и может помочь определить, с каким адресом вы имеете дело.
String ipPattern = "(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}):(\\d+)";
String ipV6Pattern = "\\[([a-zA-Z0-9:]+)\\]:(\\d+)";
String hostPattern = "([\\w\\.\\-]+):(\\d+)"; // note will allow _ in host name
Pattern p = Pattern.compile( ipPattern + "|" + ipV6Pattern + "|" + hostPattern );
Matcher m = p.matcher( someString );
if( m.matches() ) {
if( m.group(1) != null ) {
// group(1) IP address, group(2) is port
} else if( m.group(3) != null ) {
// group(3) is IPv6 address, group(4) is port
} else if( m.group(5) != null ) {
// group(5) is hostname, group(6) is port
} else {
// Not a valid address
}
}
Изменение так, чтобы порт был необязательным, довольно прямолинейный. Оберните ": (\ d +)" как "(?:: (\ d +))?" и затем проверьте значение null для группы (2) и т.д.
Изменить: я буду замечать, что нет способа "общего пути", о котором я знаю, но выше, как я это сделал бы, если бы мне пришлось.
Также обратите внимание: случай IPv4 может быть удален, если на самом деле и хост и IPv4 будут обрабатываться одинаково. Я разделяю их, потому что иногда вы можете избежать окончательного поиска хоста, если знаете, что у вас есть IP-адрес.
Ответ 5
new InetSocketAddress(
addressString.substring(0, addressString.lastIndexOf(":")),
Integer.parseInt(addressString.substring(addressString.lastIndexOf(":")+1, addressString.length));
? Вероятно, я сделал какую-то глупую ошибку. и я предполагаю, что вам нужен только новый объект InetSocketAddress из String только в этом формате. хост: порт
Ответ 6
Все виды своеобразных хакерских атак и элегантные, но небезопасные решения, представленные в других местах. Иногда неумелое решение грубой силы - путь.
public static InetSocketAddress parseInetSocketAddress(String addressAndPort) throws IllegalArgumentException {
int portPosition = addressAndPort.length();
int portNumber = 0;
while (portPosition > 1 && Character.isDigit(addressAndPort.charAt(portPosition-1)))
{
--portPosition;
}
String address;
if (portPosition > 1 && addressAndPort.charAt(portPosition-1) == ':')
{
try {
portNumber = Integer.parseInt(addressAndPort.substring(portPosition));
} catch (NumberFormatException ignored)
{
throw new IllegalArgumentException("Invalid port number.");
}
address = addressAndPort.substring(0,portPosition-1);
} else {
portNumber = 0;
address = addressAndPort;
}
return new InetSocketAddress(address,portNumber);
}
Ответ 7
Java-библиотека с открытым исходным кодом IPAddress имеет класс HostName, который будет выполнять необходимый анализ. Отказ от ответственности: я руководитель проекта библиотеки IPAddress.
Он будет анализировать IPv4, IPv6 и имена хостов строк с портами или без них. Он будет обрабатывать все различные форматы хостов и адресов. Кстати, для этого не существует единого RFC, есть несколько RFC, которые применяются по-разному.
String hostName = "[a:b:c:d:e:f:a:b]:8080";
String hostName2 = "1.2.3.4:8080";
String hostName3 = "a.com:8080";
try {
HostName host = new HostName(hostName);
host.validate();
InetSocketAddress address = host.asInetSocketAddress();
HostName host2 = new HostName(hostName2);
host2.validate();
InetSocketAddress address2 = host2.asInetSocketAddress();
HostName host3 = new HostName(hostName3);
host3.validate();
InetSocketAddress address3 = host3.asInetSocketAddress();
// use socket address
} catch (HostNameException e) {
String msg = e.getMessage();
// handle improperly formatted host name or address string
}