`
java2000.net
  • 浏览: 631263 次
  • 性别: Icon_minigender_1
  • 来自: 天津
社区版块
存档分类
最新评论

Java技术体验,HTTP多线程下载,端口侦听和自启动服务

阅读更多

一个网友正好需要这个东西,我就把几个技术整合到了一起。包括三个部分,实现时也是逐个做到的
  1. 多线程的文件下载,HTTP协议
  2. 把这个功能做成一个HTTP的服务,侦听在某个端口上,方便非Java的系统使用
  3. 把这个功能封装为一个Windows服务,在机器启动时可以自动启动

我们逐个看程序。

一、多线程下载

这个主要使用了HTTP协议里面的一个Range参数,他设置了你读取数据的其实位置和终止位置。 经常使用flashget的用户在查看连接的详细信息时,应该经常看到这个东西。比如

Range:bytes=100-2000

代表从100个字节的位置开始读取,到2000个字节的位置结束,应读取1900个字节。

程序首先拿到文件的长度,然后分配几个线程去分别读取各自的一段,使用了
RandomAccessFile
进行随机位置的读写。

下面是完整的下载的代码。
  1. package net.java2000.tools;
  2. import java.io.BufferedInputStream;
  3. import java.io.File;
  4. import java.io.IOException;
  5. import java.io.RandomAccessFile;
  6. import java.net.URL;
  7. import java.net.URLConnection;
  8. /**
  9.  * HTTP的多线程下载工具。
  10.  * 
  11.  * @author 赵学庆 www.java2000.net
  12.  */
  13. public class HTTPDownloader extends Thread {
  14.   // 要下载的页面
  15.   private String page;
  16.   // 保存的路径
  17.   private String savePath;
  18.   // 线程数
  19.   private int threadNumber = 2;
  20.   // 来源地址
  21.   private String referer;
  22.   // 最小的块尺寸。如果文件尺寸除以线程数小于这个,则会减少线程数。
  23.   private int MIN_BLOCK = 10 * 1024;
  24.   public static void main(String[] args) throws Exception {
  25.     HTTPDownloader d = new HTTPDownloader("http://www.xxxx.net/xxxx.rar""d://xxxx.rar"10);
  26.     d.down();
  27.   }
  28.   public void run() {
  29.     try {
  30.       down();
  31.     } catch (Exception e) {
  32.       e.printStackTrace();
  33.     }
  34.   }
  35.   /**
  36.    * 下载操作
  37.    * 
  38.    * @throws Exception
  39.    */
  40.   public void down() throws Exception {
  41.     URL url = new URL(page); // 创建URL
  42.     URLConnection con = url.openConnection(); // 建立连接
  43.     int contentLen = con.getContentLength(); // 获得资源长度
  44.     if (contentLen / MIN_BLOCK + 1 < threadNumber) {
  45.       threadNumber = contentLen / MIN_BLOCK + 1// 调整下载线程数
  46.     }
  47.     if (threadNumber > 10) {
  48.       threadNumber = 10;
  49.     }
  50.     int begin = 0;
  51.     int step = contentLen / threadNumber;
  52.     int end = 0;
  53.     for (int i = 0; i < threadNumber; i++) {
  54.       end += step;
  55.       if (end > contentLen) {
  56.         end = contentLen;
  57.       }
  58.       new HTTPDownloaderThread(this, i, begin, end).start();
  59.       begin = end;
  60.     }
  61.   }
  62.   public HTTPDownloader() {
  63.   }
  64.   /**
  65.    * 下载
  66.    * 
  67.    * @param page 被下载的页面
  68.    * @param savePath 保存的路径
  69.    */
  70.   public HTTPDownloader(String page, String savePath) {
  71.     this(page, savePath, 10);
  72.   }
  73.   /**
  74.    * 下载
  75.    * 
  76.    * @param page 被下载的页面
  77.    * @param savePath 保存的路径
  78.    * @param threadNumber 线程数
  79.    */
  80.   public HTTPDownloader(String page, String savePath, int threadNumber) {
  81.     this(page, page, savePath, 10);
  82.   }
  83.   /**
  84.    * 下载
  85.    * 
  86.    * @param page 被下载的页面
  87.    * @param savePath 保存的路径
  88.    * @param threadNumber 线程数
  89.    * @param referer 来源
  90.    */
  91.   public HTTPDownloader(String page, String referer, String savePath, int threadNumber) {
  92.     this.page = page;
  93.     this.savePath = savePath;
  94.     this.threadNumber = threadNumber;
  95.     this.referer = referer;
  96.   }
  97.   public String getPage() {
  98.     return page;
  99.   }
  100.   public void setPage(String page) {
  101.     this.page = page;
  102.   }
  103.   public String getSavePath() {
  104.     return savePath;
  105.   }
  106.   public void setSavePath(String savePath) {
  107.     this.savePath = savePath;
  108.   }
  109.   public int getThreadNumber() {
  110.     return threadNumber;
  111.   }
  112.   public void setThreadNumber(int threadNumber) {
  113.     this.threadNumber = threadNumber;
  114.   }
  115.   public String getReferer() {
  116.     return referer;
  117.   }
  118.   public void setReferer(String referer) {
  119.     this.referer = referer;
  120.   }
  121. }
  122. /**
  123.  * 下载线程
  124.  * 
  125.  * @author 赵学庆 www.java2000.net
  126.  */
  127. class HTTPDownloaderThread extends Thread {
  128.   HTTPDownloader manager;
  129.   int startPos;
  130.   int endPos;
  131.   int id;
  132.   int curPos;
  133.   int BUFFER_SIZE = 4096;
  134.   int readByte = 0;
  135.   HTTPDownloaderThread(HTTPDownloader manager, int id, int startPos, int endPos) {
  136.     this.id = id;
  137.     this.manager = manager;
  138.     this.startPos = startPos;
  139.     this.endPos = endPos;
  140.   }
  141.   public void run() {
  142.     // System.out.println("线程" + id + "启动");
  143.     // 创建一个buff
  144.     BufferedInputStream bis = null;
  145.     RandomAccessFile fos = null;
  146.     // 缓冲区大小
  147.     byte[] buf = new byte[BUFFER_SIZE];
  148.     URLConnection con = null;
  149.     try {
  150.       File file = new File(manager.getSavePath());
  151.       // 创建RandomAccessFile
  152.       fos = new RandomAccessFile(file, "rw");
  153.       // 从startPos开始
  154.       fos.seek(startPos);
  155.       // 创建连接,这里会为每个线程都创建一个连接
  156.       URL url = new URL(manager.getPage());
  157.       con = url.openConnection();
  158.       con.setAllowUserInteraction(true);
  159.       curPos = startPos;
  160.       // 设置获取资源数据的范围,从startPos到endPos
  161.       con.setRequestProperty("Range""bytes=" + startPos + "-" + endPos);
  162.       // 盗链解决
  163.       con.setRequestProperty("referer", manager.getReferer() == null ? manager.getPage() : manager.getReferer());
  164.       con.setRequestProperty("userAgent""Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)");
  165.       // 下面一段向根据文件写入数据,curPos为当前写入的未知,这里会判断是否小于endPos,
  166.       // 如果超过endPos就代表该线程已经执行完毕
  167.       bis = new BufferedInputStream(con.getInputStream());
  168.       while (curPos < endPos) {
  169.         int len = bis.read(buf, 0, BUFFER_SIZE);
  170.         if (len == -1) {
  171.           break;
  172.         }
  173.         fos.write(buf, 0, len);
  174.         curPos = curPos + len;
  175.         if (curPos > endPos) {
  176.           // 获取正确读取的字节数
  177.           readByte += len - (curPos - endPos) + 1;
  178.         } else {
  179.           readByte += len;
  180.         }
  181.       }
  182.       // System.out.println("线程" + id + "已经下载完毕:" + readByte);
  183.       bis.close();
  184.       fos.close();
  185.     } catch (IOException ex) {
  186.       ex.printStackTrace();
  187.     }
  188.   }
  189. }


二、做成Http的服务,侦听某个端口
使用了JDK6的特性,大家自己看代码吧,并不复杂
  1. package net.java2000.tools;
  2. import java.io.IOException;
  3. import java.io.OutputStream;
  4. import java.net.InetSocketAddress;
  5. import java.net.URLDecoder;
  6. import java.util.regex.Matcher;
  7. import java.util.regex.Pattern;
  8. import com.sun.net.httpserver.HttpExchange;
  9. import com.sun.net.httpserver.HttpHandler;
  10. import com.sun.net.httpserver.HttpServer;
  11. import com.sun.net.httpserver.spi.HttpServerProvider;
  12. /**
  13.  * 下载程序的服务版本。<br>
  14.  * 可以供其它程序调用,而不是局限于java
  15.  * 
  16.  * @author 赵学庆 www.java2000.net
  17.  */
  18. public class HTTPDownloadService {
  19.   /**
  20.    * @param args
  21.    * @throws IOException
  22.    */
  23.   public static void main(String[] args) throws IOException {
  24.     HttpServerProvider httpServerProvider = HttpServerProvider.provider();
  25.     int port = 60080;
  26.     if (args.length > 0) {
  27.       try {
  28.         port = Integer.parseInt(args[0]);
  29.       } catch (Exception ex) {}
  30.     }
  31.     // 绑定端口
  32.     InetSocketAddress addr = new InetSocketAddress(port);
  33.     HttpServer httpServer = httpServerProvider.createHttpServer(addr, 1);
  34.     httpServer.createContext("/"new HTTPDownloaderServiceHandler());
  35.     httpServer.setExecutor(null);
  36.     httpServer.start();
  37.     System.out.println("started");
  38.   }
  39. }
  40. /**
  41.  * 下载的服务器
  42.  * 
  43.  * @author 赵学庆 www.java2000.net
  44.  */
  45. class HTTPDownloaderServiceHandler implements HttpHandler {
  46.   private static Pattern p = Pattern.compile("\\?page=(.*?)\\&rpage=(.*?)&savepath=(.*)$");
  47.   public void handle(HttpExchange httpExchange) {
  48.     try {
  49.       Matcher m = p.matcher(httpExchange.getRequestURI().toString());
  50.       String response = "OK";
  51.       if (m.find()) {
  52.         try {
  53.           new HTTPDownloader(URLDecoder.decode(m.group(1), "GBK"), URLDecoder.decode(m.group(2), "GBK"), URLDecoder.decode(m.group(3), "GBK"), 10).start();
  54.         } catch (Exception e) {
  55.           response = "ERROR -1";
  56.           e.printStackTrace();
  57.         }
  58.       } else {
  59.         response = "ERROR 1";
  60.       }
  61.       httpExchange.sendResponseHeaders(200, response.getBytes().length);
  62.       OutputStream out = httpExchange.getResponseBody();
  63.       out.write(response.getBytes());
  64.       out.close();
  65.       httpExchange.close();
  66.     } catch (Exception ex) {
  67.       ex.printStackTrace();
  68.     }
  69.   }
  70. }

这个程序可以单独运行的,通过
http://127.0.0.1:60080
访问,参数是固定顺序的,比如
http://127.0.0.1:60080?page=http://www.csdn.net&rpage=http://blog.csdn.net&savepath=d:/csdn.html

三个参数分别代表了
page = 被下载的页面
rpage = referer的页面,用来对付防盗链的系统
savepath = 下载后保存的文件




三、改造成Windows的服务

主要使用了这个技术,不是很复杂 http://www.java2000.net/p598


所需要的东西我已经全部提供,且提供了一个完整的zip包供下载学习和研究用。


下载地址和原文请看这里: http://www.java2000.net/p9398




总结:
  希望对web变成有兴趣的,应该多了解一下HTTP协议,对于提高你的认知层次,注意不是水平,而是层次是有极大的帮助的。
















<script type="text/javascript"> </script> <script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript"> </script>
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics