web-dev-qa-db-fra.com

Java: moyen courant de valider et de convertir "Host: port" en InetSocketAddress?

Quelle est la méthode habituelle en Java pour valider et convertir une chaîne de la forme Host:port en une instance de InetSocketAddress?

Ce serait bien si les critères suivants étaient remplis:

  • Aucune recherche d'adresse;

  • Travailler pour IPv4, IPv6 et les noms d'hôtes "chaîne";
    (Pour IPv4, il s'agit de ip:port. Pour IPv6, il s'agit de [ip]:port, n'est-ce pas? Existe-t-il un RFC définissant tous ces schémas?)

  • Préférable sans analyser la chaîne à la main.
    (Je pense à tous ces cas spéciaux, quand quelqu'un pense connaître toutes les formes valables d'adresses de socket, mais oublie "ce cas spécial" qui conduit à des résultats inattendus.)

27
java.is.for.desktop

Je propose moi-même une solution de contournement possible.

Convertissez une chaîne en URI (cela la validerait automatiquement), puis interrogez les composants de l'hôte et du port de l'URI.

Malheureusement, un URI avec un composant hôte DOIT avoir un schéma. C'est pourquoi cette solution n'est "pas parfaite".

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
}

Cette solution ne nécessite aucune analyse de chaîne personnalisée , fonctionne avec IPv4 (1.1.1.1:123), IPv6 ([::0]:123) et Noms d'hôtes (my.Host.com:123).

Cette solution convient fort bien à mon scénario. J'allais utiliser des schémas d'URI de toute façon.

44
java.is.for.desktop

Une regex le fera très proprement:

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

Vous pouvez le faire de différentes manières, par exemple en rendant le port facultatif ou en effectuant une validation sur l'hôte.

8
cletus

Cela ne répond pas exactement à la question, mais cette réponse pourrait quand même être utile à d'autres personnes comme moi qui souhaitent simplement analyser un hôte et un port, mais pas nécessairement une variable complète InetAddress. Guava a un HostAndPort class avec une méthode parseString.

6
Edward Dale

Une autre personne a donné une réponse rationnelle, ce que je faisais lorsque je posais à l'origine la question sur les hôtes. Je le ferai quand même, car c’est un exemple de regex légèrement plus avancé qui peut aider à déterminer le type d’adresse à laquelle vous faites face.

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

La modification de sorte que le port est facultatif est assez simple. Enroulez le ": (\ d +)" comme "(? :: (\ d +))?" puis recherchez la valeur null pour le groupe (2), etc.

Edit: Je noterai qu’il n’existe pas de méthode "commune" à ma connaissance, mais la procédure ci-dessus indique comment procéder si j’étais obligé de le faire.

Remarque: le cas IPv4 peut être supprimé si les cas Host et IPv4 seront réellement traités de la même manière. Je les sépare, car vous pouvez parfois éviter une ultime recherche d'hôte si vous savez que vous avez l'adresse IP.

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

? J'ai probablement fait une petite erreur stupide. et je suppose que vous vouliez simplement un nouvel objet InetSocketAddress dans String, uniquement dans ce format. Port hôte

2
AFK

La bibliothèque Java IPAddress open-source a un HostName class qui effectuera l'analyse requise. Clause de non-responsabilité: Je suis le chef de projet de la bibliothèque IPAddress. 

Il analysera les noms d'hôtes IPv4, IPv6 et chaîne avec ou sans ports. Il gérera tous les différents formats d’hôtes et d’adresses. BTW, il n'y a pas de RFC unique pour cela, il y a plusieurs RFC qui s'appliquent de différentes manières.

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
}
0
Sean F

Tous les types de piratage, et des solutions élégantes mais dangereuses fournies ailleurs. Parfois, la solution inélégante de force brute est la solution.

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
Robin Davies