Javaはネットワークに強い言語として知られています。関連するクラスが豊富に提供されていることがその理由の1つとして挙げられますが、その基本である「IPアドレス」を取得する方法を紹介します。
ネットワークプログラミングをするとき,IPアドレスは随所に必要になります。最近でこそIPアドレスを直接意識するケースは減りましたが,それでも設定やメッセージに必ず登場します。プログラムから自分のIPアドレスを取得するにはどうすればよいでしょうか。今回は,この方法を紹介しましょう。
自ホストのIPアドレスを取得する目的には,ソケットオブジェクトからgetInetAddressメソッドを呼び出したり,java.net.InetAddressクラスのgetLocalHostメソッドで取得するという方法があります。これらは,比較的古くからある手法です。
System.out.println(
"My address=" + java.net.InetAddress.getLocalHost().toString());
しかし,この方法には特殊なマシン構成のときに,思いどおりのIPアドレスができるとは限らないという問題がありました。
通常のクライアントマシンの場合,(127.0.01以外の)IPアドレスは1つしか持たないことも多いですが,構成によっては複数のネットワークインタフェースカード(Network Interface Card,以下NIC),さらに1枚のNICに複数のIPアドレスを割り当てることが可能です。このような構成の場合,前述の方法ではどのIPアドレスを取得できているのかわかりません。
マシンが持つIPアドレスを取得するには,このような機器構成の柔軟さも考慮に入れなくてはならないのです。
J2SE 1.4では,java.net.NetworkInterfaceクラスが導入されたことにより,複雑な機器構成のマシンでも柔軟にIPアドレスが取得できるようになりました。このクラスはあくまでも抽象的に「NIC」を表現します。
NetworkInterface.getNetworkInterfaces()とすることでEnumerationが取得できます。このEnumerationを用いると,NetworkInterfaceクラスのインスタンスを列挙できます。そのため,次のようにすると全NetworkInterfaceをループで列挙できます。
java.util.Enumeration enuIfs = NetworkInterface.getNetworkInterfaces();
while (enuIfs.hasMoreElements())
{
NetworkInterface ni = (NetworkInterface)enuIfs.nextElement();
// NetworkInterfaceに対する処理。
}
ただし,NICを持たない環境の場合,getNetworkInterfacesメソッドはnullを返しますので注意してください。また,NICは,物理的に装着したものだけでなく,ループバックインタフェース(127.0.0.1,localhostとして利用する)のような論理的なものも認識します。
得られたNetworkInterfaceはNICを表現しますが,1つのNICが複数のIPアドレスを持つことがありますので,IPアドレスはあらためて列挙する必要があります。このループにもEnumerationを利用します。先の処理で得たNetworkInterfaceがniという変数とすると,次のようになります。
java.util.Enumeration enuAddrs = ni.getInetAddresses();
while (enuAddrs.hasMoreElements())
{
InetAddress in4 = (InetAddress)enuAddrs.nextElement();
// InetAddressに対する処理。
}
このとき,InetAddressの実体はサブクラスであるjava.net.Inet4Addressが返されるのが一般的でしょう。この2重ループにより,全インタフェースの全IPアドレスを取得できます。
まとめると,次のようになるでしょう。次の例は,自ホストのIPアドレスをすべて列挙して,表示可能な情報を出力するサンプルです。
java.util.Enumeration enuIfs = NetworkInterface.getNetworkInterfaces();
if (null != enuIfs)
{
while (enuIfs.hasMoreElements())
{
System.out.println("INTERFECE FOUND");
NetworkInterface ni = (NetworkInterface)enuIfs.nextElement();
System.out.println("getDisplayName:\t" + ni.getDisplayName());
System.out.println("getName:\t" + ni.getName());
java.util.Enumeration enuAddrs = ni.getInetAddresses();
while (enuAddrs.hasMoreElements())
{
InetAddress in4 = (InetAddress)enuAddrs.nextElement();
System.out.println("getHostAddress:\t" + in4.getHostAddress());
}
}
}
ところで,Windows XP環境でこのコードを実行させてみると,ni.getDisplayName()の結果が次のように文字化けすることがあります。
>java test
INTERFECE FOUND
getDisplayName: MS TCP Loopback interface
getName: lo
getHostAddress: 127.0.0.1
INTERFECE FOUND
getDisplayName: Intel(R) PRO/Wireless LAN 2100 3B Mini PCI Adapter
- ?p?P?b?g ?X?P?W???[?? ?~?j?|?[?g
getName: eth0
INTERFECE FOUND
getDisplayName: Intel(R) PRO/100 VM Network Connection
- ?p?P?b?g ?X?P?W???[?? ?~?j?|?[?g
getName: eth1
おそらく,システムに問い合わせる段階で直接表示に適さない内部情報を取得してきているのでしょう。そこで,試しにgetDisplayNameメソッドの戻り値を表示する部分を次のように変更して実行すると,なにやら意味のありそうな表示に変わります。
String dispName = ni.getDisplayName();
System.out.println("getDisplayName:\t" + new String(dispName.getBytes("iso-8859-1")));
次の結果を見ると,「パケット スケジューラ ミニポート」という文字が隠されていたようです。
>java test
INTERFECE FOUND
getDisplayName: MS TCP Loopback interface
getName: lo
getHostAddress: 127.0.0.1
INTERFECE FOUND
getDisplayName: Intel(R) PRO/Wireless LAN 2100 3B Mini PCI Adapter
- パケット スケジューラ ミニポート
getName: eth0
INTERFECE FOUND
getDisplayName: Intel(R) PRO/100 VM Network Connection
- パケット スケジューラ ミニポート
getName: eth1
しかし,なぜここでエンコーディングの変換が必要かという疑問は拭えません。仕様かもしれませんし不具合かもしれません。getDisplayNameを使うよりgetNameのほうが表示には無難なのかもしれないと考えると,奇妙な話です。ちなみに,動作確認はJ2SEのバージョン「1.4.2_05」で行いました。
このTipsの実用的な利用方法としては,getNameメソッドが返す「インタフェース名」を設定ファイルで記述することでコントロールするなどでしょう。または,Swingアプリケーションなどで,NICを選択するダイアログボックスを表示させてもよいでしょう。
「JAVA Developer」より毎週役立つJava Tipsを配信中。ほかにも参考になるTipsは、JAVA Developerサイトのバックナンバーから探すことが可能です。
Copyright(C) 2010 SOFTBANK Creative Inc. All Right Reserved.