JAVA基础知识之网络编程——-网络基础(多线程下载,get,post)

Posted 云中之歌

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JAVA基础知识之网络编程——-网络基础(多线程下载,get,post)相关的知识,希望对你有一定的参考价值。

本文主要介绍java.net下为网络编程提供的一些基础包,InetAddress代表一个IP协议对象,可以用来获取IP地址,Host name之类的信息。URL和URLConnect可以用来访问web资源,URLDecode和URLEncode用来转换字符串。 本文会写两个例子来演示java网络编程的一些基本用法。

第一个例子,写一个程序用来模拟多线程下载。

本例中用到的技术有,
多线程——多个线程同时读文件写文件,可以加快下载速度,

线程池——在本例中线程池不是必须,甚至是多余,只不过是为了演示线程池的简单用法

断点下载/上传——用RandomAccessFile的seek方法可以直接从某个中间位置读取或者写入文件

URLConnection——建立网络连接,读取网络数据的基本方法

iostring——网络IO,建立在URLConnection上的网络IO stream

有几个关键点值得注意,

  • 一是使用了URL的实例建立了一个httpURLConnection实例, 通过这个实例的getInputStream(继承自父类)可以返回一个instream对象进行读数据操作
  • 二是由于是多线程从远程读文件,每个文件将读取一块文件,文件大小按线程数平分,单个线程文件块 = 文件总大小 / 线程总数,使用RandomAccessFile的方式可以按指定位置读写文件
  • 使用RandomAccessFile的seek方法,计算每个线程下载的起始位置
  • 使用inStream的skip方法计算每次从网络读取数据的位置

具体实现如下, 先写一个工具类DownUtil, 其中写了一个内部线程类download,还定义了一个线程池,将多个线程类的实例提交给线程池管理,

  1 package network;
  2 
  3 import java.io.IOException;
  4 import java.io.InputStream;
  5 import java.io.RandomAccessFile;
  6 import java.net.HttpURLConnection;
  7 import java.net.MalformedURLException;
  8 import java.net.URL;
  9 import java.util.concurrent.ExecutorService;
 10 import java.util.concurrent.Executors;
 11 
 12 
 13 public class DownUtil {
 14     //下载资源路径
 15     private String path;
 16     private String targetFile;
 17     //线程数量
 18     private int threadNum;
 19     //下载线程对象
 20     private DownThread[] threads;
 21     //下载文件总大小
 22     private int fileSize;
 23     //定义一个线程池,在构造函数中初始化成具体类型的线程池
 24     ExecutorService pool;
 25     
 26     public DownUtil(String path, String targetFile, int threadNum) {
 27         this.path = path;
 28         this.targetFile = targetFile;
 29         this.threadNum = threadNum;
 30         this.threads = new DownThread[threadNum];
 31         this.pool = Executors.newFixedThreadPool(threadNum);
 32     }
 33     
 34     public void download() throws IOException {
 35         URL url = new URL(path);
 36         HttpURLConnection conn = (HttpURLConnection)url.openConnection();
 37         conn.setConnectTimeout(5*1000);
 38         conn.setRequestMethod("GET");
 39         conn.setRequestProperty("accept", "*/*");
 40         /*
 41         conn.setRequestProperty("Accept", "image/gif, image/jpg, image/png, "
 42                 + "application/x-shockwave-flash, application/xam+xml, "
 43                 + "application/vnd.ms-xpsdocument, application/x-ms-xbap, "
 44                 + "application/x-ms-application, application/vnd.ms-excel, "
 45                 + "application/vnd.ms-powerpoint, application/msword, **"); 
 46         */
 47         conn.setRequestProperty("Charset", "multipart/form-data");
 48         conn.setRequestProperty("Connection", "Keep-Alive");
 49         //得到文件大小
 50         fileSize = conn.getContentLength();
 51         conn.disconnect();
 52         //每个线程要下载的文件部分的大小
 53         int currentPartSize = fileSize / threadNum + 1;
 54         RandomAccessFile file = new RandomAccessFile(targetFile , "rw");
 55         file.setLength(fileSize);
 56         file.close();
 57         for ( int i = 0; i < threadNum; i++ ) {
 58             // 计算每个线程下载的开始位置
 59             int startPos = i * currentPartSize;
 60             //每个线程使用一个RandomAccessFile进行下载
 61             RandomAccessFile currentPart = new RandomAccessFile(targetFile, "rw");
 62             //定位该线程的下载位置
 63             currentPart.seek(startPos);
 64             //创建下载线程
 65             threads[i] = new DownThread(startPos, currentPartSize, currentPart);
 66             //将单个线程提交到线程池,线程会启动
 67             pool.submit(threads[i]);
 68         }
 69         pool.shutdown();
 70     }
 71     
 72     //获取下载的完成百分比
 73     public double getCompleteRate() {
 74         //统计多个线程已经下载的总大小
 75         int sumSize = 0;
 76         for (int i = 0; i< threadNum; i++) {
 77             sumSize += threads[i].length;
 78         }
 79         return sumSize * 1.0 / fileSize ;
 80     }
 81     
 82     private class DownThread implements Runnable {
 83         //当前线程的下载位置
 84         private int startPos;
 85         //当前线程负责下载的大小
 86         private int currentParSize;
 87         //当前线程需要下载的文件块
 88         private RandomAccessFile currentPart;
 89         //该线程一下载字节数
 90         public int length;
 91         
 92         public DownThread(int startPos, int currentPartSize,
 93                 RandomAccessFile currentPart) {
 94             this.startPos = startPos;
 95             this.currentParSize = currentPartSize;
 96             this.currentPart = currentPart;
 97         }
 98         
 99         public void run() {
100             try {
101                 URL url = new URL(path);
102                 HttpURLConnection conn = (HttpURLConnection)url.openConnection();
103                 conn.setConnectTimeout(5*1000);
104                 conn.setRequestMethod("GET");
105                 conn.setRequestProperty("accept", "*/*");
106                 /*
107                 conn.setRequestProperty("Accept", "image/gif, image/jpg, image/png, "
108                         + "application/x-shockwave-flash, application/xam+xml, "
109                         + "application/vnd.ms-xpsdocument, application/x-ms-xbap, "
110                         + "application/x-ms-application, application/vnd.ms-excel, "
111                         + "application/vnd.ms-powerpoint, application/msword, **"); 
112                 */
113                 //conn.setContentType(conn.getContentType());
114                 conn.setRequestProperty("Charset", "UTF-8");
115                 conn.setRequestProperty("Connection", "Keep-Alive");
116                 InputStream inStream = conn.getInputStream();
117                 //跳过startPos之前的内容
118                 inStream.skip(this.startPos);
119                 byte[] buffer = new byte[1024];
120                 int hasRead = 0;
121                 // 读取网络数据,写入本地文件
122                 while (length < currentParSize && (hasRead = inStream.read(buffer)) != -1) {
123                     currentPart.write(buffer, 0 ,hasRead);
124                     length += hasRead;
125                 }
126                 currentPart.close();
127                 inStream.close();
128             } catch (Exception e) {
129                 e.printStackTrace();
130             }
131         }
132     }
133 }

 在主程序中初始化工具类DownUtil,然后每隔固定时间去检查一次下载进度,

 1 package network;
 2 
 3 import java.io.IOException;
 4 
 5 public class MultiThreadDown {
 6     public static void main(String[] args) throws IOException, InterruptedException {
 7         // 初始化DownUtil
 8         final DownUtil downUtil = new DownUtil(
 9                 "http://sw.bos.baidu.com/sw-search-sp/software/7d662d80a3d85/npp_7.2_Installer.exe",
10                 "notepad.exe",4);
11         //开始下载
12         downUtil.download();
13         Thread monitor =  new Thread(new Runnable(){
14 
15             @Override
16             public void run() {
17                 while(downUtil.getCompleteRate() <= 1) {
18                     //每隔1秒查看一次完成进度
19                     System.out.println("已完成:"+downUtil.getCompleteRate());
20                     try {
21                         Thread.sleep(1000);
22                     } catch (InterruptedException e) {
23                         // TODO Auto-generated catch block
24                         e.printStackTrace();
25                     }
26                 }
27             }
28             
29         });
30         monitor.start();
31         monitor.join();
32         System.out.println("下载完成");
33     }
34 }

程序是以下载一个QQ程序为例, 下面是执行结果,

1 已完成:0.0
2 已完成:0.11293072177355859
3 已完成:0.278329429397341
4 已完成:0.4422385019008841
5 已完成:0.5001718809754122
6 已完成:0.5001718809754122
7 已完成:0.6514949778417655
8 已完成:0.9376444643781072
9 下载完成

本例中,如果调整线程个数,经过测试,线程数越多,下载反而越慢,估计是因为创建IO时间占用太多,毕竟下载一个文件本来就不用多久。 看来对于小文件下载但是单线程快。

 


以上是关于JAVA基础知识之网络编程——-网络基础(多线程下载,get,post)的主要内容,如果未能解决你的问题,请参考以下文章

Java并发编程系列之二线程基础

Java学习笔记之三十四超详解Java多线程基础

多线程编程之线程基础

JAVA基础之多线程二期

Java多线程系列--“基础篇”10之 线程优先级和守护线程

并发编程之多线程基础-守护线程与非守护线程