Java - Язык:Распространенный способ проверки и преобразования “host: port” в inetsocket address?

StackOverflow https://stackoverflow.com/questions/2345063

Вопрос

Каков распространенный способ в Java для проверки и преобразования строки вида host:port в экземпляр InetSocketAddress?

Было бы неплохо, если бы соблюдались следующие критерии:

  • Нет поиска адресов;

  • Работает с IPv4, IPv6 и "строковыми" именами хостов;
    (Для IPv4 это ip:port, для IPv6 это [ip]:port, верно?Существует ли какой-то RFC, который определяет все эти схемы?)

  • Предпочтительнее без разбора строки вручную.
    (Я думаю обо всех тех особых случаях, когда кто-то думает, что знает все допустимые формы адресов сокетов, но забывает об "этом особом случае", который приводит к неожиданным результатам.)

Это было полезно?

Решение

Я сам предлагаю одно из возможных обходных решений.

Преобразуйте строку в URI (это позволит проверить ее автоматически), а затем запросите компоненты URI host и port.

К сожалению, 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-схемы.

Другие советы

Регулярное выражение сделает это довольно аккуратно:

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

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

Это не дает точного ответа на вопрос, но этот ответ все равно может быть полезен другим, таким как я, которые просто хотят проанализировать хост и порт, но не обязательно полный InetAddress.Гуава обладает Хост - порт класс с parseString способ.

Другой человек дал ответ в виде регулярного выражения, что я и делал, когда изначально задавал вопрос о хостах.Я все равно сделаю это, потому что это пример регулярного выражения, которое немного более продвинуто и может помочь определить, с каким адресом вы имеете дело.

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 может быть удален, если регистры host и IPv4 фактически будут обрабатываться одинаково.Я разделяю их, потому что иногда вы можете избежать окончательного поиска хоста, если знаете, что у вас есть IP-адрес.

new InetSocketAddress(
  addressString.substring(0, addressString.lastIndexOf(":")),
  Integer.parseInt(addressString.substring(addressString.lastIndexOf(":")+1, addressString.length));

?Вероятно, я допустил какую-то маленькую глупую ошибку.и я предполагаю, что вы просто хотели создать новый объект inetsocket address из строки только в этом формате.хост: порт

Всевозможный своеобразный хакерство и элегантные, но небезопасные решения, предлагаемые в других местах.Иногда выход - это неэлегантное решение методом грубой силы.

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

В Java-библиотека IPAddress с открытым исходным кодом имеет Имя хоста класс, который будет выполнять требуемый синтаксический анализ.Отказ от ответственности:Я являюсь руководителем проекта библиотеки 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
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top