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