1. 程式人生 > >第十七章.網絡編程

第十七章.網絡編程

負責 定位 gen rac isp mission b- rpo rop

Java的基本網絡支持:

  使用InetAddress:

技術分享
 1 import java.net.InetAddress;
 2 
 3 public class InetAddressTest{
 4     public static void main(String[] args) throws Exception{
 5         //根據主機名來獲取對應的InetAddress實例
 6         InetAddress ip = InetAddress.getByName("www.lanshanxiao.cc");
 7         //判斷是否可達
 8         System.out.println("lanshanxiao是否可達:" + ip.isReachable(2000));
9 //獲取該InetAddress實例的IP字符串 10 System.out.println(ip.getHostAddress()); 11 //根據原始IP地址來獲取對應的InetAddress實例 12 InetAddress local = InetAddress.getByAddress(new byte[] {127,0,0,1}); 13 System.out.println("本機是否可達:" + local.isReachable(5000)); 14 //獲取該InetAddress實例對應的權限定域名
15 System.out.println(local.getCanonicalHostName()); 16 } 17 }
View Code

技術分享

註意上面程序中:InetAddress local = InetAddress.getByAddress(new byte[] {127,0,0,1});大括號中的127,0,0,1之間的符號是逗號

  使用URLDecoder和URLEncoder:

    URLDecoder和URLEncoder用於完成普通字符串和application/x-www-form-urlencoded MIME字符串之間的相互轉換。

技術分享
 1 import
java.net.URLEncoder; 2 import java.net.URLDecoder; 3 4 public class URLDecoderTest{ 5 public static void main(String[] args) throws Exception{ 6 //將application/x-www-form-urlencoded字符串 7 //轉換成普通字符串 8 String keyWord = URLDecoder.decode("%E7%96%AF%E7%8B%82java", "utf-8"); 9 System.out.println(keyWord); 10 //將普通字符串轉換成 11 //application/x-www-form-urlencoded字符串 12 String urlStr = URLEncoder.encode("瘋狂Android講義", "GBK"); 13 System.out.println(urlStr); 14 } 15 }
View Code

技術分享

  URL、URLConnection、URLPermission:

    URL(Uniform Resource Locator)對象代表統一資源定位器,它是指向互聯網“資源”的指針。資源可以是簡單的文件或目錄,也可以是復雜的對象的引用,如:對數據庫

     或搜索引擎的查詢。

    URL可以由協議名、主機、端口、資源組成:

    protocol://host:port/resourceName

    如下:

    http://www.crazyit.org/index.php

    多線程下載工具類:

技術分享
  1 import java.io.RandomAccessFile;
  2 import java.io.InputStream;
  3 import java.net.URL;
  4 import java.net.HttpURLConnection;
  5 
  6 public class DownUtil{
  7     //定義下載資源的路徑
  8     private String path;
  9     //指定所下載的文件的保存位置
 10     private String targetFile;
 11     //定義需要使用多少個線程下載資源
 12     private int threadNum;
 13     //定義下載的線程對象
 14     private DownThread[] threads;
 15     //定義下載的文件的總大小
 16     private int fileSize;
 17     
 18     public DownUtil(String path, String targetFile, int threadNum){
 19         this.path = path;
 20         this.threadNum = threadNum;
 21         //初始化threads數組
 22         threads = new DownThread[threadNum];
 23         this.targetFile = targetFile;
 24     }
 25     
 26     public void download() throws Exception{
 27         URL url = new URL(path);
 28         HttpURLConnection conn = (HttpURLConnection) url.openConnection();
 29         conn.setConnectTimeout(5 * 1000);
 30         conn.setRequestMethod("GET");
 31         conn.setRequestProperty(
 32             "Accept",
 33             "image/gif, image/jpeg, image/pjpeg, image/pjpeg, "
 34             + "application/x-shockwave-flash, application/xaml+xml, "
 35             + "application/vnd.ms-xpsdocument, application/x-ms-xbap, "
 36             + "application/x-ms-application, application/vnd.ms-excel, "
 37             + "application/vnd.ms-powerpoint, application/msword, */*");
 38         conn.setRequestProperty("Accept-Language", "zh-CN");
 39         conn.setRequestProperty("Charset", "UTF-8");
 40         conn.setRequestProperty("Connection", "Keep-Alive");
 41         //得到文件大小
 42         fileSize = conn.getContentLength();
 43         conn.disconnect();
 44         int currentPartSize = fileSize / threadNum + 1;
 45         RandomAccessFile file = new RandomAccessFile(targetFile, "rw");
 46         //設置本地文件大小
 47         file.setLength(fileSize);
 48         file.close();
 49         for (int i = 0; i < threadNum; i++)
 50         {
 51             // 計算每條線程的下載的開始位置
 52             int startPos = i * currentPartSize;
 53             // 每個線程使用一個RandomAccessFile進行下載
 54             RandomAccessFile currentPart = new RandomAccessFile(targetFile,
 55                 "rw");
 56             // 定位該線程的下載位置
 57             currentPart.seek(startPos);
 58             // 創建下載線程
 59             threads[i] = new DownThread(startPos, currentPartSize,
 60                 currentPart);
 61             // 啟動下載線程
 62             threads[i].start();
 63         }
 64     }
 65 
 66     // 獲取下載的完成百分比
 67     public double getCompleteRate()
 68     {
 69         // 統計多條線程已經下載的總大小
 70         int sumSize = 0;
 71         for (int i = 0; i < threadNum; i++)
 72         {
 73             sumSize += threads[i].length;
 74         }
 75         // 返回已經完成的百分比
 76         return sumSize * 1.0 / fileSize;
 77     }
 78 
 79     private class DownThread extends Thread
 80     {
 81         // 當前線程的下載位置
 82         private int startPos;
 83         // 定義當前線程負責下載的文件大小
 84         private int currentPartSize;
 85         // 當前線程需要下載的文件塊
 86         private RandomAccessFile currentPart;
 87         // 定義已經該線程已下載的字節數
 88         public int length;
 89 
 90         public DownThread(int startPos, int currentPartSize,
 91             RandomAccessFile currentPart)
 92         {
 93             this.startPos = startPos;
 94             this.currentPartSize = currentPartSize;
 95             this.currentPart = currentPart;
 96         }
 97 
 98         @Override
 99         public void run()
100         {
101             try
102             {
103                 URL url = new URL(path);
104                 HttpURLConnection conn = (HttpURLConnection)url
105                     .openConnection();
106                 conn.setConnectTimeout(5 * 1000);
107                 conn.setRequestMethod("GET");
108                 conn.setRequestProperty(
109                     "Accept",
110                     "image/gif, image/jpeg, image/pjpeg, image/pjpeg, "
111                     + "application/x-shockwave-flash, application/xaml+xml, "
112                     + "application/vnd.ms-xpsdocument, application/x-ms-xbap, "
113                     + "application/x-ms-application, application/vnd.ms-excel, "
114                     + "application/vnd.ms-powerpoint, application/msword, */*");
115                 conn.setRequestProperty("Accept-Language", "zh-CN");
116                 conn.setRequestProperty("Charset", "UTF-8");
117                 InputStream inStream = conn.getInputStream();
118                 // 跳過startPos個字節,表明該線程只下載自己負責哪部分文件。
119                 inStream.skip(this.startPos);
120                 byte[] buffer = new byte[1024];
121                 int hasRead = 0;
122                 // 讀取網絡數據,並寫入本地文件
123                 while (length < currentPartSize
124                     && (hasRead = inStream.read(buffer)) != -1)
125                 {
126                     currentPart.write(buffer, 0, hasRead);
127                     // 累計該線程下載的總大小
128                     length += hasRead;
129                 }
130                 currentPart.close();
131                 inStream.close();
132             }
133             catch (Exception e)
134             {
135                 e.printStackTrace();
136             }
137         }
138     }
139 }
View Code

    程序中DownUtil類中的download()方法負責按如下步驟實現多線程下載:

      1.創建URL對象

      2.獲取指定URL對象所指向資源的大小(通過getContentLength()方法獲得),此處用到了URLConnection類,該類代表Java應用程序和URL之間的通信鏈接。

      3.在本地磁盤上創建一個與網絡資源具有相同大小的空文件。

      4.計算每個線程應該下載網絡資源的哪個部分(從哪個字節開始,到哪個字節結束)

      5.依次創建、啟動多個線程來下載網絡資源的指定部分

上面程序已經實現了多線程下載的核心代碼,若要實現斷點下載,則需要額外增加一個配置文件(讀者可以發現,所有的斷點下載工具都會在下載開始時生成兩個文件:一個是與網絡資源具有相同大小的空文件,一個是配置文件),該配置文件分別記錄每個線程已經下載到哪個字節,當網絡斷開後再次下載時,每個線程根據配置文件裏記錄的位置向後下載即可。

技術分享
 1 public class MultiThreadDown
 2 {
 3     public static void main(String[] args) throws Exception
 4     {
 5         // 初始化DownUtil對象
 6         final DownUtil downUtil = new DownUtil("http://www.crazyit.org/"
 7             + "attachments/month_1403/1403202355ff6cc9a4fbf6f14a.png"
 8             , "ios.png", 4);
 9         // 開始下載
10         downUtil.download();
11         new Thread(() -> {
12                 while(downUtil.getCompleteRate() < 1)
13                 {
14                     // 每隔0.1秒查詢一次任務的完成進度,
15                     // GUI程序中可根據該進度來繪制進度條
16                     System.out.println("已完成:"
17                         + downUtil.getCompleteRate());
18                     try
19                     {
20                         Thread.sleep(1000);
21                     }
22                     catch (Exception ex){}
23                 }
24         }).start();
25     }
26 }
View Code

  通常創建一個和URL的連接,並發送請求、讀取此URL引用的資源需要如下幾個步驟:

    1.通過調用URL對象的openConnection()方法來創建URLConnection對象

    2.設置URLConnection的參數和普通請求屬性

    3.若只是發送GET方式請求,則使用connect()方法建立和遠程資源之間的實際連接即可;若要發送POST方式的請求,則需要獲取URLConnection實例對應的輸出流來發

     送請求參數

    4.遠程資源變為可用,程序可以訪問遠程資源的頭字段或通過輸入流讀取遠程資源的數據

  若既要使用輸入流讀取URLConnection響應的內容又要使用輸出流發送請求參數,則一定要先使用輸出流,在使用輸入流。

下面程序示範了如何向Web站點發送GET請求、POST請求,並從Web站點取得響應:

技術分享
  1 import java.net.URL;
  2 import java.net.URLConnection;
  3 import java.util.Map;
  4 import java.util.List;
  5 import java.io.BufferedReader;
  6 import java.io.InputStreamReader;
  7 import java.io.PrintWriter;
  8 
  9 public class GetPostTest
 10 {
 11     /**
 12      * 向指定URL發送GET方法的請求
 13      * @param url 發送請求的URL
 14      * @param param 請求參數,格式滿足name1=value1&name2=value2的形式。
 15      * @return URL所代表遠程資源的響應
 16      */
 17     public static String sendGet(String url , String param)
 18     {
 19         String result = "";
 20         String urlName = url + "?" + param;
 21         try
 22         {
 23             URL realUrl = new URL(urlName);
 24             // 打開和URL之間的連接
 25             URLConnection conn = realUrl.openConnection();
 26             // 設置通用的請求屬性
 27             conn.setRequestProperty("accept", "*/*");
 28             conn.setRequestProperty("connection", "Keep-Alive");
 29             conn.setRequestProperty("user-agent"
 30                 , "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");
 31             // 建立實際的連接
 32             conn.connect();
 33             // 獲取所有響應頭字段
 34             Map<String, List<String>> map = conn.getHeaderFields();
 35             // 遍歷所有的響應頭字段
 36             for (String key : map.keySet())
 37             {
 38                 System.out.println(key + "--->" + map.get(key));
 39             }
 40             try(
 41                 // 定義BufferedReader輸入流來讀取URL的響應
 42                 BufferedReader in = new BufferedReader(
 43                     new InputStreamReader(conn.getInputStream() , "utf-8")))
 44             {
 45                 String line;
 46                 while ((line = in.readLine())!= null)
 47                 {
 48                     result += "\n" + line;
 49                 }
 50             }
 51         }
 52         catch(Exception e)
 53         {
 54             System.out.println("發送GET請求出現異常!" + e);
 55             e.printStackTrace();
 56         }
 57         return result;
 58     }
 59     /**
 60      * 向指定URL發送POST方法的請求
 61      * @param url 發送請求的URL
 62      * @param param 請求參數,格式應該滿足name1=value1&name2=value2的形式。
 63      * @return URL所代表遠程資源的響應
 64      */
 65     public static String sendPost(String url , String param)
 66     {
 67         String result = "";
 68         try
 69         {
 70             URL realUrl = new URL(url);
 71             // 打開和URL之間的連接
 72             URLConnection conn = realUrl.openConnection();
 73             // 設置通用的請求屬性
 74             conn.setRequestProperty("accept", "*/*");
 75             conn.setRequestProperty("connection", "Keep-Alive");
 76             conn.setRequestProperty("user-agent",
 77             "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");
 78             // 發送POST請求必須設置如下兩行
 79             conn.setDoOutput(true);
 80             conn.setDoInput(true);
 81             try(
 82                 // 獲取URLConnection對象對應的輸出流
 83                 PrintWriter out = new PrintWriter(conn.getOutputStream()))
 84             {
 85                 // 發送請求參數
 86                 out.print(param);
 87                 // flush輸出流的緩沖
 88                 out.flush();
 89             }
 90             try(
 91                 // 定義BufferedReader輸入流來讀取URL的響應
 92                 BufferedReader in = new BufferedReader(new InputStreamReader
 93                     (conn.getInputStream() , "utf-8")))
 94             {
 95                 String line;
 96                 while ((line = in.readLine())!= null)
 97                 {
 98                     result += "\n" + line;
 99                 }
100             }
101         }
102         catch(Exception e)
103         {
104             System.out.println("發送POST請求出現異常!" + e);
105             e.printStackTrace();
106         }
107         return result;
108     }
109     // 提供主方法,測試發送GET請求和POST請求
110     public static void main(String args[])
111     {
112         // 發送GET請求
113         String s = GetPostTest.sendGet("http://localhost:8888/abc/a.jsp"
114             , null);
115         System.out.println(s);
116         // 發送POST請求
117         String s1 = GetPostTest.sendPost("http://localhost:8888/abc/login.jsp"
118             , "name=crazyit.org&pass=leegang");
119         System.out.println(s1);
120     }
121 }
View Code

  這一部分需要創建Web應用,現在我還不會。但是會在接下來學習《輕量級Java EE企業應用實戰》的時候創建Web應用。Web應用的代碼我上傳到GitHub網站(會在文章最後寫出),是一個abc的文件夾。

基於TCP協議的網絡編程:

  使用ServerSocket創建TCP服務器端:

    Java中能接收其他通信實體連接請求的類是ServerSocket,ServerSocket對象用於監聽來自客戶端的Socket連接,若沒有連接,它將一直處於等待狀態。ServerSocket包含

     一個監聽來自客戶端連接請求的方法。

      1.Socket accept():若接收到一個客戶端Socket的連接請求,該方法返回一個與客戶端Socket對應的Socket;否則該方法一直處於等待狀態,線程也被阻塞

  使用Socket進行通信:

技術分享
 1 import java.net.ServerSocket;
 2 import java.net.Socket;
 3 import java.io.PrintStream;
 4 import java.io.IOException;
 5 
 6 public class Server
 7 {
 8     public static void main(String[] args)
 9         throws IOException
10     {
11         // 創建一個ServerSocket,用於監聽客戶端Socket的連接請求
12         ServerSocket ss = new ServerSocket(30000);
13         // 采用循環不斷接受來自客戶端的請求
14         while (true)
15         {
16             // 每當接受到客戶端Socket的請求,服務器端也對應產生一個Socket
17             Socket s = ss.accept();
18             // 將Socket對應的輸出流包裝成PrintStream
19             PrintStream ps = new PrintStream(s.getOutputStream());
20             // 進行普通IO操作
21             ps.println("您好,您收到了服務器的新年祝福!");
22             // 關閉輸出流,關閉Socket
23             ps.close();
24             s.close();
25         }
26     }
27 }
View Code 技術分享
 1 import java.net.Socket;
 2 import java.io.BufferedReader;
 3 import java.io.InputStreamReader;
 4 import java.io.IOException;
 5 
 6 public class Client
 7 {
 8     public static void main(String[] args)
 9         throws IOException
10     {
11         Socket socket = new Socket("127.0.0.1" , 30000);   //12         // 將Socket對應的輸入流包裝成BufferedReader
13         BufferedReader br = new BufferedReader(
14         new InputStreamReader(socket.getInputStream()));
15         // 進行普通IO操作
16         String line = br.readLine();
17         System.out.println("來自服務器的數據:" + line);
18         // 關閉輸入流、socket
19         br.close();
20         socket.close();
21     }
22 }
View Code

  在Windows系統下分別用兩個cmd,一個運行Server端,一個運行Client端,先運行Server端:

技術分享

技術分享

第十七章.網絡編程