第十七章.网络编程

Posted lanshanxiao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第十七章.网络编程相关的知识,希望对你有一定的参考价值。

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

以上是关于第十七章.网络编程的主要内容,如果未能解决你的问题,请参考以下文章

第十七章.网络编程

《Linux命令行与shell脚本编程大全》第十七章 创建函数

《构建之法》(第十七章)读书笔记

信息安全-第十七章-网络安全应急响应

读《构建之法》第四章 第十七章

第十七章 Python网络编程