我竟然到現在才發現《Fundamental Networking in Java》這本神作,真有點無地自容的感覺。最近幾年做的都是所謂的企業級開發,免不了和網絡打交道,但在實際工作中,往往會采用框架將底層細節和上層應用隔離開,感覺就像是在一個 Word 模板表單里面填寫內容,做出來也沒什么成就感。雖然沒有不使用框架的理由,但我還真是有點懷念當初直接用套接字做網絡編程的日子,既能掌控更多東西,還可以學到更多知識,為研究框架的實現原理打基礎。閑話完畢,轉入今天的正題:IP(Internet Protocol,互聯網協議)。
IP 基礎知識
說到 IP,大多數人的第一反應估計都是 IP 地址。其實 IP 是一種協議,IP 地址只是協議的一部分。《RFC 791 - INTERNET PROTOCOL》說:“互聯網協議是為在包交換計算機通信網絡的互聯系統中使用而設計的。”IP 包含三方面的功能:
- 用于查找主機的尋址系統
- 包格式的定義
- 傳輸和接收包的規則
IP 的相關 Java 類
從 Java 的角度來看上面說到的三個功能,只有第一個是開發人員需要關心的。另外兩個都依賴底層系統的實現,JDK 也沒有提供相關的類去操作。下面一一介紹 JDK 提供的用于處理 IP 地址的類。
InetAddress
此類用來表示 IP 地址,它有兩個子類:Inet4Address
和 Inet6Address
,分別用于處理 IPv4 和 IPv6 兩個版本。在實際應用中,InetAddress
足以應付絕大多數情況。它提供了一些靜態方法來構造實例,能根據參數格式自動識別 IP 版本:
public static InetAddress[] getAllByName(String host) throws UnknownHostException
- 解析指定的主機地址,并返回其所有的 IP 地址;如果傳入 IP 地址字符串,則只會校驗格式,返回的數組也只包含一個代表該 IP 地址的實例。例如,想看看谷歌有多少馬甲的話,
InetAddress.getAllByName("www.google.com")
就可以了。
public static InetAddress getByAddress(byte[] addr) throws UnknownHostException
- 用表示 IP 地址的字節數組(專業術語稱為“原始 IP 地址”)構造一個實例。IPv4 地址必須是 4 個字節,IPv6 必須 16 個。不常用。
public static InetAddress getByAddress(String host, byte[] addr) throws UnknownHostException
- 用主機地址和原始 IP 地址構造一個實例。此方法應該慎用,因為它不會對主機名進行解析。即使主機名為 IP 地址字符串,也不會檢查是否與字節數組一致。
public static InetAddress getByName(String host) throws UnknownHostException
- 用主機地址構造一個實例,也可以直接傳入 IP 地址字符串,等同于
getAllByName(host)[0]
。
public static InetAddress getLocalHost() throws UnknownHostException
- 返回本機在網絡中的地址。
public static InetAddress getLoopbackAddress()
- 返回環回地址
127.0.0.1
,不拋出異常,等同于 getByName("localhost")
(不要和 getLocalHost()
搞混)。環回地址使主機能夠自己連接自己,常被用來對在同一臺機器上測試網絡應用程序。在 IPv4 中,環回地址的網段為 127.0.0.0/8
,通常用 127.0.0.1
;IPv6 中只有一個 ::1
。
接下來看看 InetAddress
中定義的部分實例方法:
public byte[] getAddress()
- 返回原始 IP 地址。
public String getCanonicalHostName()
- 返回全限定域名。這個方法可以用來探查實際的主機名,例如
InetAddress.getByName("www.google.com").getCanonicalHostName()
返回 we-in-f99.1e100.net。
public String getHostAddress()
- 返回構造時傳入的主機地址。
public String getHostName()
- 返回主機名。如果構造時傳入的主機地址為 IP 地址字符串,則調用
getCanonicalHostName()
,否則直接返回構造時傳入的主機地址。
public boolean isAnyLocalAddress()
- 檢查是否為通配符地址。通配符地址為
0.0.0.0
(IPv4)或 ::0
(IPv6),代表所有的本地 IP 地址。例如,假設電腦有兩塊網卡,各有一個地址,如果想讓一個程序同時監聽這兩個地址,就需要用通配符地址。
public boolean isLinkLocalAddress()
- 檢查是否為鏈路本地地址。IPv4 里定義為地址段
169.254.0.0/16
,Ipv6 里是以 fe80::/64
為前綴的地址。在電腦沒聯網的時候查看本機 IP,就能看到這種地址。
public boolean isLoopbackAddress()
- 檢查是否為環回地址。
public boolean isSiteLocalAddress()
- 檢查是否為站點本地地址。站點本地地址這個名詞實際上已經過時了,現在叫唯一本地地址。IPv4 中未定義;IPv6 中定義為地址段
fc00::/7
。這些地址用于私有網絡,例如企業內部的局域網。
此外還有一些有關多播地址的方法,暫時略過。
JDK 默認同時支持 IPv4 和 IPv6。如果只想使用一種,可以根據情況將 java.net.preferIPv4Stack
或 java.net.preferIPv6Addresses
這兩個系統屬性之一設為 true
。兩個屬性的默認值都為 false
。一般來說不需要去驚動它們。
SocketAddress
該類是一個空殼,事實上應用程序使用的是它的唯一子類 InetSocketAddress
,目前還看不出這樣設計有什么意義。該類只不過在 InetAddress
的基礎上增加了一個端口屬性。
NetworkInterface
該類代表網絡接口,例如一塊網卡。一個網絡接口可以綁定一些 IP 地址。具有多個網絡接口的主機被稱為多宿主主機。下面的代碼可打印出所有本機網絡接口的信息:
for (NetworkInterface ni : Collections.list(NetworkInterface.getNetworkInterfaces())) {
System.out.println(ni);
for (InterfaceAddress ia : ni.getInterfaceAddresses()) {
System.out.println("\t" + ia);
}
System.out.println();
}
在我的筆記本上運行結果為:
name:lo (Software Loopback Interface 1)
/127.0.0.1/8 [/127.255.255.255]
/0:0:0:0:0:0:0:1/128 [null]
name:net0 (WAN Miniport (SSTP))
name:net1 (WAN Miniport (L2TP))
name:net2 (WAN Miniport (PPTP))
name:ppp0 (WAN Miniport (PPPOE))
name:eth0 (WAN Miniport (IPv6))
name:eth1 (WAN Miniport (Network Monitor))
name:eth2 (WAN Miniport (IP))
name:ppp1 (RAS Async Adapter)
name:net3 (WAN Miniport (IKEv2))
name:net4 (Intel(R) Wireless WiFi Link 4965AGN)
/fe80:0:0:0:288a:2daf:3549:1811%11/64 [null]
name:eth3 (Broadcom NetXtreme 57xx Gigabit Controller)
/10.140.1.133/24 [/10.140.1.255]
/fe80:0:0:0:78c7:e420:1739:f947%12/64 [null]
name:net5 (Teredo Tunneling Pseudo-Interface)
/fe80:0:0:0:e0:0:0:0%13/64 [null]
name:net6 (Bluetooth Device (RFCOMM Protocol TDI))
name:eth4 (Bluetooth Device (Personal Area Network))
name:eth5 (Cisco AnyConnect VPN Virtual Miniport Adapter for Windows x64)
name:net7 (Microsoft ISATAP Adapter)
/fe80:0:0:0:0:5efe:a8c:185%17/128 [null]
name:net8 (Microsoft ISATAP Adapter #2)
name:net9 (Intel(R) Wireless WiFi Link 4965AGN-QoS Packet Scheduler-0000)
name:eth6 (Broadcom NetXtreme 57xx Gigabit Controller-TM NDIS Sample LightWeight Filter-0000)
name:eth7 (Broadcom NetXtreme 57xx Gigabit Controller-QoS Packet Scheduler-0000)
name:eth8 (Broadcom NetXtreme 57xx Gigabit Controller-WFP LightWeight Filter-0000)
name:eth9 (WAN Miniport (Network Monitor)-QoS Packet Scheduler-0000)
name:eth10 (WAN Miniport (IP)-QoS Packet Scheduler-0000)
name:eth11 (WAN Miniport (IPv6)-QoS Packet Scheduler-0000)
name:net10 (Intel(R) Wireless WiFi Link 4965AGN-Native WiFi Filter Driver-0000)
name:net11 (Intel(R) Wireless WiFi Link 4965AGN-TM NDIS Sample LightWeight Filter-0000)
name:net12 (Intel(R) Wireless WiFi Link 4965AGN-WFP LightWeight Filter-0000)