Java: ¿Forma común de validar y convertir "host: port" a InetSocketAddress?


¿Cuál es la forma común en Java para validar y convertir una cadena de la forma host:port en una instancia de InetSocketAddress?

Sería bueno que se cumplieran los siguientes criterios:

  • No hay búsquedas de direcciones;

  • Trabajando para nombres de host IPv4, IPv6 y "string" ;
    (Para IPv4 es ip:port, para IPv6 es [ip]:port, ¿verdad? ¿Hay algún RFC que defina todos estos esquemas?)

  • Preferible sin analizar la cadena a mano.
    (Soy pensando en todos esos casos especiales, cuando alguien piensa que conoce todas las formas válidas de direcciones de socket, pero se olvida de "ese caso especial" que conduce a resultados inesperados.)

Author: java.is.for.desktop, 2010-02-27

6 answers

Yo mismo propongo una posible solución alternativa.

Convierte una cadena en URI (esto la validaría automáticamente) y luego consulta los componentes host y port del URI.

Lamentablemente, un URI con un componente host DEBE tener un scheme. Esta es la razón por la que esta solución "no es perfecta".

String string = ... // some string which has to be validated
String host;
int port;

try {
  // WORKAROUND: add any scheme to make the resulting URI valid.
  URI uri = new URI("my://" + string); // may throw URISyntaxException
  host = uri.getHost();
  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, ...

} catch (URISyntaxException ex) {
  // validation failed
}

// validation succeeded

Esta solución no necesita un análisis personalizado de cadenas, funciona con IPv4 (1.1.1.1:123), IPv6 ([::0]:123) y nombres de host (my.host.com:123).

Accidentalmente, esta solución es muy adecuada para mi escenario. Iba a usar esquemas URI de todos modos.

 44
Author: java.is.for.desktop,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-03-01 18:53:26

Una expresión regular hará esto muy bien:

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

Puede hacer esto de muchas maneras, como hacer que el puerto sea opcional o hacer alguna validación en el host.

 8
Author: cletus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-27 00:33:51

No responde a la pregunta exactamente, pero esta respuesta aún podría ser útil para otros como yo que solo quieren analizar un host y un puerto, pero no necesariamente un InetAddress completo. Guava tiene una clase HostAndPort con un método parseString.

 6
Author: scompt.com,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-27 13:55:15

Otra persona ha dado una respuesta regular que es lo que yo estaba haciendo al hacer originalmente la pregunta sobre los anfitriones. Todavía lo haré porque es un ejemplo de una expresión regular que es un poco más avanzada y puede ayudar a determinar con qué tipo de dirección está tratando.

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

Modificar para que el puerto sea opcional es bastante sencillo. Envuelva el ":(\d+)" como "(?:: (\d+))?"y luego comprobar si hay null para group (2), etc.

Editar: Notaré que no hay una manera" común" de eso soy consciente, pero lo anterior es cómo lo haría si tuviera que hacerlo.

También tenga en cuenta: el caso IPv4 se puede eliminar si el host y los casos IPv4 realmente se manejarán de la misma manera. Los divido porque a veces puede evitar una búsqueda definitiva de host si sabe que tiene la dirección IP.

 5
Author: PSpeed,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-03-10 05:44:46
new InetSocketAddress(
  addressString.substring(0, addressString.lastIndexOf(":")),
  Integer.parseInt(addressString.substring(addressString.lastIndexOf(":")+1, addressString.length));

? Probablemente cometí algún pequeño error tonto. y estoy asumiendo que sólo quería un nuevo objeto InetSocketAddress fuera de la cadena en sólo ese formato. host: port

 2
Author: AFK,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-16 13:32:48

Todo tipo de hacker peculiar, y soluciones elegantes pero inseguras proporcionadas en otros lugares. A veces la solución de fuerza bruta poco elegante es el camino.

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);
}
 0
Author: Robin Davies,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-22 21:19:48