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包供下载学习和研究用。
多线程的文件下载,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包供下载学习和研究用。
相关文章推荐
- Java技术体验,HTTP多线程下载,端口侦听和自启动服务
- Java技术体验,HTTP多线程下载,端口侦听和自启动服务
- Java——HTTP多线程下载,端口侦听和自启动服务
- Java——HTTP多线程下载,端口侦听和自启动服务
- Jenkins启动报端口被占用,解决办法FAILED ServerConnector@2a265ea9{HTTP/1.1}{0.0.0.0:8080}: java
- Windows server 2008 HTTP.sys被禁用导致80端口被禁用和IIS服务无法启动解决办法
- JavaWeb服务启动时,在后台启动加载一个线程进行Socket监听端口
- 搭建IIS服务器80端口却已被占用的问题和用了Sc config http start= disabled命令导致HTTP服务无法启动的解决方案
- Win7/Vista/Server2008下VS 环境 调试调用 HTTP.SYS 无法启动监听服务及启动后其他机器无法访问端口
- lync边缘服务无法启动,因为端口被IIS占用
- java扫描指定主机的端口socket服务
- 在linux上以服务的方式启动java程序
- wamp5 服务启动不起,在网页中输入http://lwww.dedecms.com/dede/会报错
- 用 Java 技术创建 RESTful Web 服务
- 启动WCF服务时出现WCF Error: HTTP could not register URL http://+:xxxx/... Your process does not have access rights to this namespace
- java通过amox-http发布http服务
- linux java服务启动脚本
- 启动Java程序为Linux服务
- Java HTTP方式请求.NET WebService服务总结
- 启动Tomcat服务报错,端口占用解决方案