我竟然到現(xiàn)在才發(fā)現(xiàn)《Fundamental Networking in Java》這本神作,真有點無地自容的感覺。最近幾年做的都是所謂的企業(yè)級開發(fā),免不了和網(wǎng)絡(luò)打交道,但在實際工作中,往往會采用框架將底層細(xì)節(jié)和上層應(yīng)用隔離開,感覺就像是在一個 Word 模板表單里面填寫內(nèi)容,做出來也沒什么成就感。雖然沒有不使用框架的理由,但我還真是有點懷念當(dāng)初直接用套接字做網(wǎng)絡(luò)編程的日子,既能掌控更多東西,還可以學(xué)到更多知識,為研究框架的實現(xiàn)原理打基礎(chǔ)。閑話完畢,轉(zhuǎn)入今天的正題:IP(Internet Protocol,互聯(lián)網(wǎng)協(xié)議)。
IP 基礎(chǔ)知識
說到 IP,大多數(shù)人的第一反應(yīng)估計都是 IP 地址。其實 IP 是一種協(xié)議,IP 地址只是協(xié)議的一部分。《RFC 791 - INTERNET PROTOCOL》說:“互聯(lián)網(wǎng)協(xié)議是為在包交換計算機通信網(wǎng)絡(luò)的互聯(lián)系統(tǒng)中使用而設(shè)計的。”IP 包含三方面的功能:
- 用于查找主機的尋址系統(tǒng)
- 包格式的定義
- 傳輸和接收包的規(guī)則
IP 的相關(guān) Java 類
從 Java 的角度來看上面說到的三個功能,只有第一個是開發(fā)人員需要關(guān)心的。另外兩個都依賴底層系統(tǒng)的實現(xiàn),JDK 也沒有提供相關(guān)的類去操作。下面一一介紹 JDK 提供的用于處理 IP 地址的類。
InetAddress
此類用來表示 IP 地址,它有兩個子類:Inet4Address
和 Inet6Address
,分別用于處理 IPv4 和 IPv6 兩個版本。在實際應(yīng)用中,InetAddress
足以應(yīng)付絕大多數(shù)情況。它提供了一些靜態(tài)方法來構(gòu)造實例,能根據(jù)參數(shù)格式自動識別 IP 版本:
public static InetAddress[] getAllByName(String host) throws UnknownHostException
- 解析指定的主機地址,并返回其所有的 IP 地址;如果傳入 IP 地址字符串,則只會校驗格式,返回的數(shù)組也只包含一個代表該 IP 地址的實例。例如,想看看谷歌有多少馬甲的話,
InetAddress.getAllByName("www.google.com")
就可以了。 public static InetAddress getByAddress(byte[] addr) throws UnknownHostException
- 用表示 IP 地址的字節(jié)數(shù)組(專業(yè)術(shù)語稱為“原始 IP 地址”)構(gòu)造一個實例。IPv4 地址必須是 4 個字節(jié),IPv6 必須 16 個。不常用。
public static InetAddress getByAddress(String host, byte[] addr) throws UnknownHostException
- 用主機地址和原始 IP 地址構(gòu)造一個實例。此方法應(yīng)該慎用,因為它不會對主機名進(jìn)行解析。即使主機名為 IP 地址字符串,也不會檢查是否與字節(jié)數(shù)組一致。
public static InetAddress getByName(String host) throws UnknownHostException
- 用主機地址構(gòu)造一個實例,也可以直接傳入 IP 地址字符串,等同于
getAllByName(host)[0]
。 public static InetAddress getLocalHost() throws UnknownHostException
- 返回本機在網(wǎng)絡(luò)中的地址。
public static InetAddress getLoopbackAddress()
- 返回環(huán)回地址
127.0.0.1
,不拋出異常,等同于getByName("localhost")
(不要和getLocalHost()
搞混)。環(huán)回地址使主機能夠自己連接自己,常被用來對在同一臺機器上測試網(wǎng)絡(luò)應(yīng)用程序。在 IPv4 中,環(huán)回地址的網(wǎng)段為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()
- 返回構(gòu)造時傳入的主機地址。
public String getHostName()
- 返回主機名。如果構(gòu)造時傳入的主機地址為 IP 地址字符串,則調(diào)用
getCanonicalHostName()
,否則直接返回構(gòu)造時傳入的主機地址。 public boolean isAnyLocalAddress()
- 檢查是否為通配符地址。通配符地址為
0.0.0.0
(IPv4)或::0
(IPv6),代表所有的本地 IP 地址。例如,假設(shè)電腦有兩塊網(wǎng)卡,各有一個地址,如果想讓一個程序同時監(jiān)聽這兩個地址,就需要用通配符地址。 public boolean isLinkLocalAddress()
- 檢查是否為鏈路本地地址。IPv4 里定義為地址段
169.254.0.0/16
,Ipv6 里是以fe80::/64
為前綴的地址。在電腦沒聯(lián)網(wǎng)的時候查看本機 IP,就能看到這種地址。 public boolean isLoopbackAddress()
- 檢查是否為環(huán)回地址。
public boolean isSiteLocalAddress()
- 檢查是否為站點本地地址。站點本地地址這個名詞實際上已經(jīng)過時了,現(xiàn)在叫唯一本地地址。IPv4 中未定義;IPv6 中定義為地址段
fc00::/7
。這些地址用于私有網(wǎng)絡(luò),例如企業(yè)內(nèi)部的局域網(wǎng)。
此外還有一些有關(guān)多播地址的方法,暫時略過。
JDK 默認(rèn)同時支持 IPv4 和 IPv6。如果只想使用一種,可以根據(jù)情況將 java.net.preferIPv4Stack
或 java.net.preferIPv6Addresses
這兩個系統(tǒng)屬性之一設(shè)為 true
。兩個屬性的默認(rèn)值都為 false
。一般來說不需要去驚動它們。
SocketAddress
該類是一個空殼,事實上應(yīng)用程序使用的是它的唯一子類 InetSocketAddress
,目前還看不出這樣設(shè)計有什么意義。該類只不過在 InetAddress
的基礎(chǔ)上增加了一個端口屬性。
NetworkInterface
該類代表網(wǎng)絡(luò)接口,例如一塊網(wǎng)卡。一個網(wǎng)絡(luò)接口可以綁定一些 IP 地址。具有多個網(wǎng)絡(luò)接口的主機被稱為多宿主主機。下面的代碼可打印出所有本機網(wǎng)絡(luò)接口的信息:
for (NetworkInterface ni : Collections.list(NetworkInterface.getNetworkInterfaces())) { System.out.println(ni); for (InterfaceAddress ia : ni.getInterfaceAddresses()) { System.out.println("\t" + ia); } System.out.println(); }
在我的筆記本上運行結(jié)果為:
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)