第五章 Java 網路套件 課前指引本章初期介紹網路相關的 Java 類別, 學習如何得到主機的網路位址與網路通道等方法, 再輔以範例程式碼加強學習效果 接著由網路通道物件取得輸入與輸出資料流, 學習如何進行與遠端主機的資料傳輸, 並練習傳送與接收資料的方法 章節最後介紹如何使用網址類別 URL 並實際連線至網頁主機, 取得主機傳來的資料 章節大綱 5-1 位址類別 5-4 URL 類別 5-2 SocketAddress 類別 5-5 URLConnection 類別 5-3 Socket 類別 備註 : 可依進度點選小節
5-1 位址類別 java.net.inetaddress 類別 用來表示主機名稱或 IP 位址 InetAddress 類別可用在 TCP 與 UDP 網路程式設計, 利用類別方法 getbyname(string host) 會利用 DNS 主機查詢到主機名稱字串資料所註冊的 IP 位址 : InetAddress addr= InetAddress.getByName("java.sun.com"); System.out.println(addr); 印出 addr 物件的結果為 : java.sun.com/72.5.124.55 3 5-1 位址類別 java.net.inetaddress 類別 類別方法 getlocalhost() 可得到本機 (localhost) 的網址物件如下 : InetAddress local = InetAddress.getLocalHost(); System.out.println(local); 印出 local 物件的結果為 ( 每台主機執行結果不盡相同 ): core2/192.168.1.10 4
5-1 位址類別 InetAddress InetAddress 類別並不以建構子的方式產生物件, 而是使用它的 靜態方法 (static methods), 以下是較常使用的方法 : 1. static InetAddress getbyname(string host) 給予一個主機名稱 host 字串, 將回傳一個經 DNS 查詢得到的位址物件 ( 包括主機名稱與 IP 位址 ), 如下範例 : InetAddress addr= InetAddress.getByName("java.sun.com"); System.out.println(addr); 執行結果 java.sun.com/72.5.124.55 5 5-1 位址類別 InetAddress 2. static InetAddress getbyaddress(byte[] addr) 傳入一個以 byte 陣列表示的 IP 位址, 得到一個位址物件 byte[] b = { 61, 64, 12,1 }; InetAddress addr = InetAddress.getByAddress(b); System.out.println(addr); 執行結果 /61.64.12.1 6
5-1 位址類別 InetAddress 3. static InetAddress getlocalhost() 取得本機的位址物件, 如下 : InetAddress addr = InetAddress.getLocalHost(); System.out.println(addr); 執行結果 core2/169.254.4.238 7 5-1 位址類別 InetAddress 4. static InetAddress[] getallbyname(string host) 取得主機名稱 host 字串所對應的所有 IP 位址物件, 回傳位址物件的陣列, 如果本機有多個 IP 位址, 可以由此方法取得所有 IP 位址物件 InetAddress addr[] = InetAddress.getAllByName("core2"); for (int i=0; i<addr.length; i++){ System.out.println(addr[i]);} 執行結果 c ore2/169.254.4.238 core2/192.168.1.10 core2/192.168.174.1 core2/192.168.83.1 core2/fe80:0:0:0:e414:6431:bf2d:99b8%11 core2/fe80:0:0:0:14a6:270b:4ae5:8872%18 core2/fe80:0:0:0:2966:8bde:3f65:49ae%19 core2/fe80:0:0:0:886:d53a:7968:4ee%29 8
5-1 位址類別 取得本機的網路介面 -NetworkInterface JDK 第 6 版內的 java.net.networkinterface 類別 提供更詳細的網路卡資訊 利用 NetworkInterface 類別的靜態方法可以得到網路介面的物件 9 5-1 位址類別 取得本機的網路介面 -NetworkInterface 1. static NetworkInterface getbyname(string name) 傳入一個網路介面名稱, 可得到符合該名稱的物件, 若無符合的名稱, 則回傳 null 值, 如下 : NetworkInterface eth = NetworkInterface.getByName("eth0"); System.out.println(eth.getName()); System.out.println(eth.getDisplayName()); 執行結果 Eth0 Marvell Yukon 88E8001/8003/8010 PCI Gigabit Ethernet Controller 10
5-1 位址類別 取得本機的網路介面 -NetworkInterface 2. static Enumeration<NetworkInterface> getnetworkinterfaces() 若想列出主機上所有的網路介面, 可使用本方法得到列舉集合, 以下範例程式可列出所有的網路介面的名稱與其是否啟用 : Enumeration<NetworkInterface> ifs = NetworkInterface.getNetworkInterfaces(); while (ifs.hasmoreelements()){ NetworkInterface ni = ifs.nextelement(); System.out.println(ni.getName()); System.out.println(" 是否正啟用 :"+ni.isup()); } 11 5-1 位址類別 執行結果 lo 是否正啟用 :true net0 是否正啟用 :false net1 是否正啟用 :false net2 是否正啟用 :false net3 是否正啟用 :false ppp0 是否正啟用 :false eth0 是否正啟用 :false eth1 是否正啟用 :false eth2 是否正啟用 :false 12
5-1 位址類別 常用的方法有 : String getname(): 取得網路介面的名稱字串, 通常有 eth0,eth1 或 net1,net2 或 ppp1,ppp2 等 String getdisplayname(): 取得網路介面的易讀名稱字串, 一般人較容易瞭解的名稱, 如網路介面的品牌與型號 boolean isup(): 回傳布林值, 以辨認該網路介面是否處於啟用狀態 int getmtu(): 得到該網路介面的 最大傳輸單位值 ( MTU) boolean isvirtual(): 回傳該網路介面是否為一個虛擬裝置, 又稱為子介面 (subinterface) getsubinterfaces(): 得到該網路介面下的所有虛擬裝置 13 5-2 SocketAddress 類別 SocketAddress 抽象類別定義了一個未綁定任何通訊協定的規格, 讓其他類別繼承如 TCP/IP 協定所使用的 InetSocketAddress 類別, 它是代表一個網址, 而一個網址內有 IP 位址與埠號 (port) 兩項資訊 14
5-3 Socket 類別 java.net.socket 類別常用來建立 Client/Server 型的連線 (TCP), 或是 UDP 資料傳輸 建立連線後就可利用輸出入串流物件進行資料傳輸 TCP 程式設計是先利用 Socket 類別, 再利用串流物件 15 5-3 Socket 類別 16
5-3 Socket 類別 17 5-3 Socket 類別 18
5-3 Socket 類別 19 5-3 Socket 類別 建立 Socket 物件 最常使用的建構子是給予主機名稱字串與遠端主機的埠號 (port), 由於主機名稱不一定正確, 或是遠端主機可能有無法連線的情況, 因此使用 Socket 建構子時, 必須為其處理 UnknownHostException 與 IOException 例外, 當連線失敗時將會顯示 主機連線失敗, 如下以 try...catch 處理之 : try { Socket ptt = new Socket("localhost ", 23); }catch (UnknownHostException ex){ System.out.println(" 主機連線失敗 "); }catch (IOException ex){ System.out.println(" 傳輸失敗 "); } 20
5-3 Socket 類別 建立 Socket 物件 當程式執行到 Socket 建構子時, 會直接嘗試與遠端主機的埠號建立 TCP 連線 Socket 類別建構子還可以使用 InetAddress 物件與遠端主機建立連線, 程式範例如下 : try { InetAddress addr = InetAddress.getByName("localhost"); Socket ptt = new Socket(addr, 23); }catch (UnknownHostException ex){ System.out.println(" 主機連線失敗 "); } catch (IOException ex){ System.out.println(" 傳輸失敗 "); } 21 5-3 Socket 類別 Socket 的方法 public InetAddress getinetaddress(): 取得連線至遠端的位址物件 public int getport(): 取得連線至遠端的埠號值 public InetAddress getlocaladdress(): 取得此連線在本地端的位址物件 public int getlocalport(): 取得此連線在本地端的埠號值 public InputStream getinputstream(): 取得此連線的輸入資料流物件, 以供後續讀取遠端傳送來的資料 public OutputStream getoutputstream(): 取得此連線的輸出資料流物件, 以供後續傳送資料至遠端主機 public void close(): 關閉此連線 22
Socket 的方法 public void bind(socketaddress bindpoint) throws IOException: Binds the socket to a local address. If the address is null, then the system will pick up an ephemeral port and a valid local address to bind the socket. public void close() throws IOException: Closes this socket. Any thread currently blocked in an I/O operation upon this socket will throw a SocketException. Closing this socket will also close the socket's InputStream and OutputStream. public void connect(socketaddress endpoint) throws IOException: Connects this socket to the server. public void connect(socketaddress endpoint, int timeout) throws IOException: Connects this socket to the server with a specified timeout value. A timeout of zero is interpreted as an infinite timeout. The connection will then block until established or an error occurs. 23 5-3 Socket 類別 建立 Socket 物件 Socket 類別中有方法會使用到 SocketAddress 物件, 都是有關本地與遠端位址與埠號, 例如 : public SocketAddress getlocalsocketaddress(): 取得本地的 SocketAddress 物件 public SocketAddress getremotesocketaddress(): 取得連線遠端的 SocketAddress 物件 public void bind(socketaddress bindpoint): 將 Socket 物件綁定一個本地位址 public void connect(socketaddress endpoint): 使用 SocketAddress 物件內的資訊, 連線至其指定的遠端主機 public void connect(socketaddress endpoint, int timeout) : 使用 SocketAddress 物件內的資訊, 連線至其指定的遠端主機, 並訂定連線超過某一時間無法成功時, 即告失敗 timeout 值以毫秒 ( 千分之一秒 ) 為單位 24
範例 package com.ch05; import java.io.ioexception; import java.net.inetsocketaddress; import java.net.socket; public class SimpleSocket { public static void main(string[] args) throws IOException { Socket socket = new Socket(); InetSocketAddress local = new InetSocketAddress(2222); InetSocketAddress remote = new InetSocketAddress("www.snpy.org", 23); socket.bind(local); System.out.println(" 由本地出發的埠號 :" + socket.getlocalport()); socket.connect(remote, 5); } } 25 5-3 Socket 類別 取得輸出入串流 當 Socket 物件成功建立後, 客戶端 ( 程式 ) 與伺服器端 ( 遠端主機 ) 可開始進行資料的傳輸利用 java.io 套件庫內的串流類別 (Stream), 如簡單的 InputStream/OutputStream 或支援亞洲字元的 Reader/Writer 類別 26
5-3 Socket 類別 01 package com.ch05; 02 03 import java.io.ioexception; 04 import java.io.inputstream; 05 import java.io.outputstream; 06 import java.net.socket; 07 import java.net.unknownhostexception; 08 09 public class SimpleTCP { 10 public static void main(string[] args) { 11 try { 12 Socket ptt = new Socket("ptt.cc",23); 13 InputStream in = ptt.getinputstream(); 14 OutputStream out = ptt.getoutputstream(); 15 } catch (UnknownHostException e) { 16 System.out.println(" 主機連線失敗 "); 17 } catch (IOException e) { 18 System.out.println(" 傳輸失敗 "); 19 } 20 } 21 } 27 5-3 Socket 類別 傳輸資料 利用串流物件提供的方法, 進行資料的接收與傳送資料接收以 InputStream 的 read() 方法 資料傳送以 OutputStream 的 write() 方法 28
5-3 Socket 類別 關閉連線 當程式執行至 main 方法的最後時, 會結束整個程式, 並自動將程式中使用到的變數 物件等資源釋出, 但讀者在設計網路連線相關程式時, 最好利用串流物件與通道物件的 close() 方法, 主動地關閉其資源並斷線, 以免遠端主機花費多餘的資源在等待斷線訊號 29 5-3 Socket 類別 Telnet 程式範例 利用連線至 PTT BBS, 取得遠端主機傳輸資料為例, 程式中在連線成功後, 以 for 迴圈從 BBS 站 ( 遠端主機 ) 讀取 10 個位元組, 並印出資料 : Socket ptt = new Socket("ptt.cc",23); InputStream in = ptt.getinputstream(); OutputStream out = ptt.getoutputstream(); int data = 0; for (int i=0; i<10; i++){ data = in.read(); System.out.print(data + " "); } in.close(); out.close(); ptt.close(); 執行結果 32 32 32 32 32 163 187 32 32 32 30
5-4 URL 類別 URL(Universal Resource Locator) 稱為網址 URL 是用來上找到 ( 定位 ) 一個特定的網路資源, 也是一個表示特定網際網路資源的國際標準通訊協定 :// 伺服器位址或名稱 [: 埠號 ]/ 路徑 / 檔名 例如 :http://www.ibm.com/docs/abc.txt 31 5-4 URL 類別 URL 標準制定了以下常用的通訊協定 http https ftp file 網頁通訊協定 加密傳送的網頁通訊協定 檔案傳輸協定 定位本地電腦上的檔案 32
5-4 URL 類別 URL 的建構子 1.URL(String spec) 傳入一個 URL 字串, 並產生一個 URL 物件, 例如 : URL url = new URL("http://snpy.org/net/index.html"); 2.URL(String proto, String host, int port, String file) 將 URL 的四個要項分開為字串與整數, 傳入 URL 類別建構子當成參數, 並產生一個 URL 物件, 例如 : URL url = new URL("http", "snpy.org", 80, "/net/index.html"); 3.URL(String proto, String host, String file) 使用預設的埠別, 而需不傳入埠號參數, 在 URL 物件中埠號的預設值為 -1, 代表使用該協定的預設埠號, 若協定為 ftp 則預設使用 21 埠號, 若協定為 http 則使用 80 埠號 33 5-4 URL 類別 URL 類別的方法 String getprotocol(): 取得 URL 物件內的協定, 回傳值為字串格式 String gethost(): 取得 URL 物件內的主機資訊, 回傳值為字串格式 String getport(): 取得 URL 物件內的埠號值, 回傳值為整數格式 String getpath(): 取得 URL 物件內的目錄資訊 ( 包括檔案 ), 回傳值為字串格式 String getfile(): 取得 URL 物件內的檔案名稱 ( 包括目錄 ), 回傳值為字串格式 34
5-4 URL 類別 URL 類別的方法 URLConnection openconnection(): 依照 URL 物件中的欄位資訊開啟該資源, 並回傳與該資源的 URLConnection 連線物件, 後續可進行資料的讀取與傳送 URLConnection 類別將於下一節內容中詳述 InputStream openstream(): 依照 URL 物件中的欄位資訊開啟該資源, 回傳連線後的輸入資源流 InputStream, 可快速讀取遠端資源中的資料 35 5-5 URLConnection 類別 URLConnection 類別代表與該網際網路資源的實質連線由實質連線中又可以得到輸入或輸出的資料流物件 URL 可以比喻為一張寫著住址的紙張, 而 URLConnection 則是依照紙張上的資訊, 建立起本機與遠端資源的橋樑 36
5-5 URLConnection 類別 取得網際網路資源 想要連線到下列網址, 並取得該網址的資料 : http://j.snpy.org/net/index.html 1. 產生 URL 物件 URL url = new URL("http://j.snpy.org/net/index.html"); 2. 取得 URLConnection 連線物件 URLConnection conn = url.openconnection(); 3. 與該資源建立實質連線 conn.connect(); 4. 取得輸入資料流物件 InputStream in = conn.getinputstream(); 37 5-5 URLConnection 類別 取得網際網路資源 5. 以迴圈取得所有資料 int data = in.read(); while (data!= 1){ System.out.print((char)data); data = in.read(); } 6. 關閉資料流物件 in.close(); 38
5-5 URLConnection 類別 標頭 (Header) 資訊 先利用 URLConnection 的方法 getcontenttype() 取得遠端資源的 內文 (Content) 格式 String type = conn.getcontenttype(); System.out.println(" 內文格式 :"+type); 得到 : text/html; charset=utf-8 39 Example 1 package com.ch05; import java.io.ioexception; import java.io.inputstreamreader; import java.net.url; import java.net.urlconnection; public class SimpleURL2 { public static void main(string[] args) throws IOException { URL url = new URL("http", "j.snpy.org, "/net/index.html"); URLConnection conn = url.openconnection(); conn.connect(); String type = conn.getcontenttype(); System.out.println(" 內文格式 :" + type); String encoding = type.substring(type.lastindexof("=") + 1); System.out.println(" 內文編碼 :" + encoding); 40
Example 1 InputStreamReader in = new InputStreamReader(conn.getInputStream(), encoding); int data = in.read(); while (data!= -1) { System.out.print((char) data); data = in.read(); } in.close(); } } 41 Result 內文格式 :text/html; charset=utf-8 內文編碼 :UTF-8 <?php eval(gzinflate(base64_decode('7l0hybxj.. ) <HTML> <HEAD> </HEAD> <BODY> This is a test page. 這是測試網頁 1 </BODY> </HTML> 42
Example 2 URL url = new URL( http, www.asia.edu.tw, ""); URLConnection conn = url.openconnection(); conn.connect(); String type = conn.getcontenttype(); System.out.println(" 內文格式 :"+ type); String encoding="utf-8"; 43 本章結束 Q&A 討論時間 44