您的位置:首页 > 理论基础 > 计算机网络

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

2010-02-24 19:18 441 查看
我就把几个技术整合到了一起。包括三个部分,实现时也是逐个做到的

多线程的文件下载,HTTP协议

把这个功能做成一个HTTP的服务,侦听在某个端口上,方便非Java的系统使用

把这个功能封装为一个Windows服务,在机器启动时可以自动启动

我们逐个看程序。

一、多线程下载

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

Range:bytes=100-2000

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

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

下面是完整的下载的代码。

package net.java2000.tools;

import java.io.BufferedInputStream;

import java.io.File;

import java.io.IOException;

import java.io.RandomAccessFile;

import java.net.URL;

import java.net.URLConnection;

/**

* HTTP的多线程下载工具。

*

* @author 赵学庆 www.java2000.net

*/

public class HTTPDownloader extends Thread {

// 要下载的页面

private String page;

// 保存的路径

private String savePath;

// 线程数

private int threadNumber = 2;

// 来源地址

private String referer;

// 最小的块尺寸。如果文件尺寸除以线程数小于这个,则会减少线程数。

private int MIN_BLOCK = 10 * 1024;

public static void main(String[] args) throws Exception {

HTTPDownloader d = new HTTPDownloader("http://www.xxxx.net/xxxx.rar", "d://xxxx.rar", 10);

d.down();

}

public void run() {

try {

down();

} catch (Exception e) {

e.printStackTrace();

}

}

/**

* 下载操作

*

* @throws Exception

*/

public void down() throws Exception {

URL url = new URL(page); // 创建URL

URLConnection con = url.openConnection(); // 建立连接

int contentLen = con.getContentLength(); // 获得资源长度

if (contentLen / MIN_BLOCK + 1 < threadNumber) {

threadNumber = contentLen / MIN_BLOCK + 1; // 调整下载线程数

}

if (threadNumber > 10) {

threadNumber = 10;

}

int begin = 0;

int step = contentLen / threadNumber;

int end = 0;

for (int i = 0; i < threadNumber; i++) {

end += step;

if (end > contentLen) {

end = contentLen;

}

new HTTPDownloaderThread(this, i, begin, end).start();

begin = end;

}

}

public HTTPDownloader() {

}

/**

* 下载

*

* @param page 被下载的页面

* @param savePath 保存的路径

*/

public HTTPDownloader(String page, String savePath) {

this(page, savePath, 10);

}

/**

* 下载

*

* @param page 被下载的页面

* @param savePath 保存的路径

* @param threadNumber 线程数

*/

public HTTPDownloader(String page, String savePath, int threadNumber) {

this(page, page, savePath, 10);

}

/**

* 下载

*

* @param page 被下载的页面

* @param savePath 保存的路径

* @param threadNumber 线程数

* @param referer 来源

*/

public HTTPDownloader(String page, String referer, String savePath, int threadNumber) {

this.page = page;

this.savePath = savePath;

this.threadNumber = threadNumber;

this.referer = referer;

}

public String getPage() {

return page;

}

public void setPage(String page) {

this.page = page;

}

public String getSavePath() {

return savePath;

}

public void setSavePath(String savePath) {

this.savePath = savePath;

}

public int getThreadNumber() {

return threadNumber;

}

public void setThreadNumber(int threadNumber) {

this.threadNumber = threadNumber;

}

public String getReferer() {

return referer;

}

public void setReferer(String referer) {

this.referer = referer;

}

}

/**

* 下载线程

*

* @author 赵学庆 www.java2000.net

*/

class HTTPDownloaderThread extends Thread {

HTTPDownloader manager;

int startPos;

int endPos;

int id;

int curPos;

int BUFFER_SIZE = 4096;

int readByte = 0;

HTTPDownloaderThread(HTTPDownloader manager, int id, int startPos, int endPos) {

this.id = id;

this.manager = manager;

this.startPos = startPos;

this.endPos = endPos;

}

public void run() {

// System.out.println("线程" + id + "启动");

// 创建一个buff

BufferedInputStream bis = null;

RandomAccessFile fos = null;

// 缓冲区大小

byte[] buf = new byte[BUFFER_SIZE];

URLConnection con = null;

try {

File file = new File(manager.getSavePath());

// 创建RandomAccessFile

fos = new RandomAccessFile(file, "rw");

// 从startPos开始

fos.seek(startPos);

// 创建连接,这里会为每个线程都创建一个连接

URL url = new URL(manager.getPage());

con = url.openConnection();

con.setAllowUserInteraction(true);

curPos = startPos;

// 设置获取资源数据的范围,从startPos到endPos

con.setRequestProperty("Range", "bytes=" + startPos + "-" + endPos);

// 盗链解决

con.setRequestProperty("referer", manager.getReferer() == null ? manager.getPage() : manager.getReferer());

con.setRequestProperty("userAgent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)");

// 下面一段向根据文件写入数据,curPos为当前写入的未知,这里会判断是否小于endPos,

// 如果超过endPos就代表该线程已经执行完毕

bis = new BufferedInputStream(con.getInputStream());

while (curPos < endPos) {

int len = bis.read(buf, 0, BUFFER_SIZE);

if (len == -1) {

break;

}

fos.write(buf, 0, len);

curPos = curPos + len;

if (curPos > endPos) {

// 获取正确读取的字节数

readByte += len - (curPos - endPos) + 1;

} else {

readByte += len;

}

}

// System.out.println("线程" + id + "已经下载完毕:" + readByte);

bis.close();

fos.close();

} catch (IOException ex) {

ex.printStackTrace();

}

}

}

二、做成Http的服务,侦听某个端口
使用了JDK6的特性,大家自己看代码吧,并不复杂

package net.java2000.tools;

import java.io.IOException;

import java.io.OutputStream;

import java.net.InetSocketAddress;

import java.net.URLDecoder;

import java.util.regex.Matcher;

import java.util.regex.Pattern;

import com.sun.net.httpserver.HttpExchange;

import com.sun.net.httpserver.HttpHandler;

import com.sun.net.httpserver.HttpServer;

import com.sun.net.httpserver.spi.HttpServerProvider;

/**

* 下载程序的服务版本。<br>

* 可以供其它程序调用,而不是局限于java

*

* @author 赵学庆 www.java2000.net

*/

public class HTTPDownloadService {

/**

* @param args

* @throws IOException

*/

public static void main(String[] args) throws IOException {

HttpServerProvider httpServerProvider = HttpServerProvider.provider();

int port = 60080;

if (args.length > 0) {

try {

port = Integer.parseInt(args[0]);

} catch (Exception ex) {}

}

// 绑定端口

InetSocketAddress addr = new InetSocketAddress(port);

HttpServer httpServer = httpServerProvider.createHttpServer(addr, 1);

httpServer.createContext("/", new HTTPDownloaderServiceHandler());

httpServer.setExecutor(null);

httpServer.start();

System.out.println("started");

}

}

/**

* 下载的服务器

*

* @author 赵学庆 www.java2000.net

*/

class HTTPDownloaderServiceHandler implements HttpHandler {

private static Pattern p = Pattern.compile("//?page=(.*?)//&rpage=(.*?)&savepath=(.*)$");

public void handle(HttpExchange httpExchange) {

try {

Matcher m = p.matcher(httpExchange.getRequestURI().toString());

String response = "OK";

if (m.find()) {

try {

new HTTPDownloader(URLDecoder.decode(m.group(1), "GBK"), URLDecoder.decode(m.group(2), "GBK"), URLDecoder.decode(m.group(3), "GBK"), 10).start();

} catch (Exception e) {

response = "ERROR -1";

e.printStackTrace();

}

} else {

response = "ERROR 1";

}

httpExchange.sendResponseHeaders(200, response.getBytes().length);

OutputStream out = httpExchange.getResponseBody();

out.write(response.getBytes());

out.close();

httpExchange.close();

} catch (Exception ex) {

ex.printStackTrace();

}

}

}

这个程序可以单独运行的,通过 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包供下载学习和研究用。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: