33

host:portフォームの文字列を検証してのインスタンスに変換するJavaの一般的な方法は何InetSocketAddressですか?

次の基準が満たされていると便利です。

  • アドレスルックアップはありません。

  • IPv4、IPv6、および「文字列」ホスト名で機能します。
    (IPv4の場合はそうですが、 ip:portIPv6の場合[ip]:portはそうです。これらすべてのスキームを定義するRFCはありますか?)

  • 文字列を手で解析せずに実行することをお勧めします。
    (誰かがソケットアドレスのすべての有効な形式を知っていると思うが、予期しない結果につながる「その特殊なケース」を忘れたとき、私はそれらすべての特殊なケースについて考えています。)

4

8 に答える 8

55

私自身、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
}

このソリューションは、カスタム文字列解析を必要とせずIPv41.1.1.1:123)、IPv6[::0]:123)、およびホスト名my.host.com:123)で機能します。

偶然にも、このソリューションは私のシナリオに非常に適しています。とにかくURIスキームを使うつもりでした。

于 2010-02-27T12:50:48.217 に答える
8

正規表現はこれを非常にうまく行います:

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

これは、ポートをオプションにする、ホストで検証を行うなど、さまざまな方法で実行できます。

于 2010-02-27T00:33:51.007 に答える
7

質問に正確に答えるわけではありませんが、この答えは、ホストとポートを解析したいだけで、必ずしも完全ではない私のような他の人にも役立つ可能性がありますInetAddress。Guavaには、メソッドを持つHostAndPortクラスがありparseStringます。

于 2012-11-27T13:55:15.527 に答える
6

別の人が、ホストについて最初に質問したときに私が行っていたことである正規表現の回答をしました。これは、少し高度な正規表現の例であり、処理しているアドレスの種類を判別するのに役立つため、引き続き実行します。

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 +))?」としてラップします。次に、group(2)などのnullをチェックします。

編集:私が知っている「一般的な方法」はないことに注意しますが、上記は私がしなければならなかった場合の方法です。

また、ホストとIPv4のケースが実際に同じように処理される場合は、IPv4のケースを削除できます。IPアドレスを知っていれば、最終的なホストルックアップを回避できる場合があるため、これらを分割しました。

于 2010-02-27T10:10:35.787 に答える
2
new InetSocketAddress(
  addressString.substring(0, addressString.lastIndexOf(":")),
  Integer.parseInt(addressString.substring(addressString.lastIndexOf(":")+1, addressString.length));

?私はおそらく少しばかげた間違いをしました。そして、その形式でのみ文字列から新しいInetSocketAddressオブジェクトが必要だったと思います。ホスト:ポート

于 2010-02-26T21:53:57.407 に答える
0

あらゆる種類の独特なハッカリー、および他の場所で提供されるエレガントだが安全でないソリューション。時々、エレガントでないブルートフォースソリューションが道です。

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);
}
于 2018-07-22T21:19:48.827 に答える
0

オープンソースのIPAddressJavaライブラリには、必要な解析を行う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
}
于 2019-01-06T05:53:38.490 に答える
0

URIはこれを達成できます:

URI uri = new URI(null, "example.com:80", null, null, null);

残念ながら、現在のOpenJDK(またはドキュメント)には、権限が適切に検証されていないバグがあります。ドキュメントには次のように記載されています。

結果のURI文字列は、URI(String)コンストラクターを呼び出してから、結果に対してparseServerAuthority()メソッドを呼び出すかのように解析されます。

残念ながら、parseServerAuthorityの呼び出しは行われないため、ここで適切に検証する実際のソリューションは次のとおりです。

URI uri = new URI(null, "example.com:80", null, null, null).parseServerAuthority();

それから

InetSocketAddress address = new InetSocketAddress(uri.getHost(), uri.getPort());
于 2021-01-29T22:39:52.983 に答える