InetAddress類概述與實例

絕大部分知識與實例來自O’REILLY的《Java網絡編程》(Java Network Programming,Fourth Edition,by Elliotte Rusty Harold(O’REILLY))。

InetAddress類簡介

InetAddress類位於java.net包中,是Java對IP地址(包括IPv4和IPv6)的高層表示。許多其他網絡相關類,包括Socket、ServerSocket、URL等都用到了這個類。這個類的每個實例包含一個IP地址(IP address)以及這個IP地址對應的主機名(host name)。

獲取InetAddress實例

InetAddress類沒有public的構造器,想要獲取它的實例,需要通過它的一些靜態工廠方法。最常用的一個是InetAddress.getByName(),能夠根據域名或者IP地址獲取InetAddress實例。這個方法會建立與本地DNS服務器的一個連接,並查找主機名和IP地址。如果DNS找不到這個地址,方法會拋出一個UnknownHostException,IOException的一個子類。

實例1:查詢某個域名的IP地址和主機名

public static void ShowAddress(String domainName){
    InetAddress address;
    try {
        address = InetAddress.getByName(domainName);
        System.out.println(address);
    } catch (UnknownHostException e) {
        System.out.println("Could not find" + domainName);
        e.printStackTrace();
    }
}

public static void main(String[] args) {
    ShowAddress("www.baidu.com");
}

輸出:
www.baidu.com/183.232.231.172

另外一個經常使用的是InetAddress.getLocalHost,用於返回運行代碼的主機對應的InetAddress對象。

實例2:查找本地機器的地址

public static void ShowLocalAddress(){
    try {
        InetAddress address = InetAddress.getLocalHost();
        System.out.println(address);
    } catch (UnknownHostException e) {
        System.out.println("Could not find this computer's address.");
    }
}

public static void main(String[] args) {
    ShowLocalAddress();
}

另外,有時一個主機名可能有多個地址,如果想要全部獲得,可以使用getAllByName()方法。該方法會返回一個InetAddress數組。
最後的兩個構造器,用於根據原始IP地址和主機名構建一個InetAddress對象:

public static InetAddress getByAddress(byte[] addr) throws UnknownHostException
public static InetAddress getByAddress(String hostName,byte[] addr) throws UnknownHostException

需要注意三點:(1)參數中的主機名不一定能和IP地址對應。(2)byte[]遵循big-endian,即IP地址的高位存儲在數組的低位。(3)Java中的byte爲有符號字節,因此數值上不一定和int的值對應(比如168轉爲byte後值爲-88)。
補充一下關於int和有符號字節的轉換:有符號字節的取值範圍爲-128~127,而8位無符號整型的取值範圍爲0~255。轉換方法如下:

  • 無符號整型轉有符號字節:0~127不變,128~255用256減去原值。
  • 有符號字節轉無符號整型:非負數不變,負數加上256。

緩存

由於DNS查找開銷很大,InetAddress類會對查找結果進行緩存,一旦得到一個給定主機的地址,就不會再次查找。這樣做可以使得可能會進行重複查找的程序的性能得到提升。不過,這種機制也帶來了一些問題,比如無法察覺主機地址的改變等。
可以通過系統屬性networkaddress.cache.ttl和networkaddress.cache.negative.ttl控制成功的DNS查找和失敗的DNS查找的結果的緩存時間,若設置爲-1則永不過期。

按IP地址查找

使用InetAddress.getByName傳入一個IP地址作爲參數時,不會檢查DNS,只有當請求主機名(getHostName())時纔會真正完成DNS查找。這意味着創建出來的InetAddress對象可能不對應任何主機。如果無法成功查找到主機名,主機名會和傳入的IP地址保持一致,而不會拋出UnknownHostException異常
相對來講,主機名比IP地址穩定的多。在構建InetAddress對象時,應優先考慮使用主機名作爲參數傳入。

get方法

InetAddress包含4個get方法,如下:

  • String getHostName():返回主機名(主機別名)
  • String getCanonicalHostName():返回主機全名
  • byte[] getAddress():獲得IP地址的原始字節碼
  • String getHostAddress():獲得IP地址

主機全名是主機的全稱,主機別名是爲了方便好記等目的,爲主機起的一個“暱稱”。下面是用www.oracle.com構建的InetAddress對象調用getHostName()和getCanonicalHostName()的結果:

www.oracle.com
a23-77-22-123.deploy.static.akamaitechnologies.com

至於查看原始字節碼的方法,很多時候主要是用來判斷協議類型(IPv4地址4字節、IPv6地址16個字節)。

實例3:判斷IP地址使用的協議版本

public static int getIpVersion(InetAddress ia){
    byte[] address = ia.getAddress();
    if(address.length == 4){
        return 4;
    }else if (address.length == 16) {
        return 6;
    }else {
        return -1;
    }
}


public static void main(String[] args) {
    try {
        InetAddress ia = InetAddress.getByName("192.168.0.0");
        System.out.println(getIpVersion(ia));
    } catch (UnknownHostException e) {
        e.printStackTrace();
    }
}

輸出:
4

判斷地址類型

有許多IP地址有着特殊的含義,比如127.0.0.1是本地回送地址,224.0.0.0~239.255.255.255是組播地址等。InetAddress類提供了以下方法對地址類別進行辨別:

  • isAnyLocalAddress():判斷是否是通配地址。IPv4中通配地址爲0.0.0.0,IPv6中通配地址爲0:0:0:0:0:0:0:0(或寫作::)。通配地址可以匹配本機系統中的任何地址。
  • isLoopbackAddress():判斷是否是回送地址。IPv4中回送地址爲127.0.0.1,IPv6中回送地址爲0:0:0:0:0:0:0:1(或寫作::1)。回送地址在網絡層連接計算機和它自身。
  • isLinkLocalAddress():判斷是否是IPv6本地鏈接地址。IPv6本地鏈接地址可以用於幫助IPv6網絡實現自配置,與IPv4網絡上的DHCP類似,但不需要使用服務器。路由器不會把發送給本地鏈接地址的包轉發到本地子網以外。所有本地連接地址都用8字節FE80:0000:0000:0000開頭,後8字節用本地地址填充,這個地址通常從以太網卡生產商分配的以太網MAC地址複製。
  • isSiteLocalAddress():判斷是否是IPv6本地網站地址。本地網站地址與本地鏈接地址類似,不過本地網站地址可以由路由器在網站或校園內轉發,但不應轉發到網站以外。本地網站地址以8字節FEC0:0000:0000:0000開頭,後8字節用本地地址填充,這個地址通常從以太網卡生產商分配的以太網MAC地址複製。
  • isMulticastAddress():判斷是否是組播地址。IPv4中組播地址範圍是224.0.0.0~239.255.255.255,IPv6中組播地址以FF開頭。
  • isMCGlobal():判斷是否是全球組播地址。全球組播地址可能在全世界都有訂購者。IPv6中全球組播地址以FF0E或FF1E開頭,取決於這個組播地址是已知的永久分配地址還是一個臨時地址。IPv4中所有組播地址都是全球範圍的,範圍依靠TTL(Time To Live,生存時間)控制。
  • isMCOrgLocal():判斷是否是組織範圍組播地址。組織範圍組播地址可能在公司或組織的所有網站都有訂購者,但不包括組織外。以FF08或FF18開頭,取決於是已知的永久分配地址還是臨時地址。
  • isMCSiteLocal():判斷是否是網站範圍組播地址。發送到網站範圍組播地址的包只會在本地網站內傳輸。以FF05或FF15開頭,取決於是已知的永久分配地址還是臨時地址。
  • isMCLinkLocal():判斷是否是子網範圍組播地址。發送到子網範圍組播地址的包只會在子網內傳輸。以FF02或FF12開頭,取決於是已知的永久分配地址還是臨時地址。
  • isMCNodeLocal():判斷是否是本地接口組播地址。發送到本地接口組播地址的包不能發送到最初的網絡接口以外,即使是相同節點上的不同網絡接口也不行,主要用於網絡調試。以FF01或FF11開頭,取決於是已知的永久分配地址還是臨時地址。

測試可達性

InetAddress類還提供了兩個isReachable()方法,用於測試節點對當前主機是否可達,原理是traceroute方法。

public boolean isReachable(int timeout) throws IOException
public boolean isReachable(NetworkInterface interface int ttl,int timeout) throws IOException

如果主機在timeout毫秒內響應則返回true,否則返回false。如果出現網絡錯誤則拋出IOException異常。第二個方法允許指定從哪個本地網絡接口建立連接,以及生存時間TTL。

Object方法

  • equals():只要兩個InetAddress對象指向同一個IP地址就返回true,不考慮主機名。
  • hashcode():同樣只根據IP地址計算。
  • toString():格式爲主機名/IP地址,若主機名不存在則將主機名置空。

Inet4Address與Inet6Address

Inet4Address與Inet6Address是InetAddress的兩個子類,用於區分IPv4地址和IPv6地址。一般來講,編程人員處於應用層,不需要了解IP地址的細節,因此這兩個子類很少用到。

NetworkInterface類

NetworkInterface類表示一個本地IP地址,可能是一個物理接口(額外的以太網卡),也可能是一個虛擬接口(與其他IP地址綁定到同一個物理硬件)。NetworkInterface提供了枚舉本地地址的方法。

示例4:列出所有網絡接口

public static void listAllNetworkInterfaces(){
    try {
        Enumeration<NetworkInterface> interfaces = 
                NetworkInterface.getNetworkInterfaces();
        while (interfaces.hasMoreElements()) {
            NetworkInterface networkInterface =
                    (NetworkInterface) interfaces.nextElement();
            System.out.println(networkInterface);
        }
    } catch (SocketException e) {
        e.printStackTrace();
    }
}

除此之外,NetworkInterface類還提供了兩個靜態工廠方法,用於根據名稱或IP地址獲取NetworkInterface對象:

public static NerworkInterface getByName(String name) throws SocketException
public static NetworkInterface getByInetAddress(InetAddress address) throws SocketException

獲得了NetworkInterface對象後,就可以利用它的一系列get方法獲取IP地址、名稱等信息。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章