java网络蚂蚁的制作流程
“网络蚂蚁”、falshget 等许多多线程下载软件都是网友的必备工具,利用这些工具可以快速从服务器上下载比较大的文件,这些工具的工作特性是把服务器端的文件分成几个段,每个段分别、同时进行下载。编写这类程序,第一、必须对http协议有较为充分的了解;第二、有效使用多线程编程手段在软件上实现。
http 协议的简介
http协议是一种超文本传输协议(hypertext transfer protocol),工作于网络应用层,自1990年起广泛应用于www 的全球信息服务,http协议的详细说明可以在网上查阅rfc2518、rfc2616等文档。
http状态码
http状态码格式是 http/版本信息的数字表示。状态码例子如下:
http/1.0 200 ok // 表示服务器支持http/1.0 协议,成功
http/1.1 200 ok // 表示服务器支持http/1.1 协议,成功
http/1.0 404 not found // 表示服务器支持http/1.0 协议,访问文件没有找到
在程序中间,如果读到“http/1.1 200 ok”这样的字符串,表明欲下载文件存在、该服务器支持断点续传,可以使用多线程下载。如果读到“http/1.0 200 ok”这样的字符串,表明欲下载文件存在、但该服务器不支持断点续传,只可以使用单线程下载。
读取重要的响应标题,获得要下载文档的文件长度
如果http状态码表明访问成功,服务器会回送一些标题行,我们最关注的是content-length 这一行,比如,如果服务器回送“content-length:1000”,表明请求文件的长度是1000字节,所以读取这一行信息,可以得到文件的长度信息:
例如:get /down.zip http/1.1
accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-
excel, application/msword, application/vnd.ms-powerpoint, */*
accept-language: zh-cn
accept-encoding: gzip, deflate
user-agent: mozilla/4.0 (compatible; msie 5.01; windows nt 5.0)
connection: keep-alive
200
content-length=106786028
accept-ranges=bytes
date=mon, 30 apr 2001 12:56:11 gmt
etag=w/"02ca57e173c11:95b"
content-type=application/octet-stream
server=microsoft-iis/5.0
last-modified=mon, 30 apr 2001 12:56:11 gmt
所谓断点续传,也就是要从文件已经下载的地方开始继续下载。所以在客户端浏览器传给
web服务器的时候要多加一条信息--从哪里开始。
下面是用自己编的一个"浏览器"来传递请求信息给web服务器,要求从2000070字节开始。
get /down.zip http/1.0
user-agent: netfox
range: bytes=2000070-
accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
仔细看一下就会发现多了一行range: bytes=2000070-
这一行的意思就是告诉服务器down.zip这个文件从2000070字节开始传,前面的字节不用传了。
服务器收到这个请求以后,返回的信息如下:
206
content-length=106786028
content-range=bytes 2000070-106786027/106786028
date=mon, 30 apr 2001 12:55:20 gmt
etag=w/"02ca57e173c11:95b"
content-type=application/octet-stream
server=microsoft-iis/5.0
last-modified=mon, 30 apr 2001 12:55:20 gmt
和前面服务器返回的信息比较一下,就会发现增加了一行:
content-range=bytes 2000070-106786027/106786028
返回的代码也改为206了,而不再是200了。
知道了以上原理,就可以进行断点续传的编程了。
分割文件,多线程下载
使用多线程编程技术,同时启动多个线程,根据线程个数,计算文件分割位置,向服务器发送几个不同的下载断点,同时接受数据并写入文件,就可以实现多线程下载了。
java实现断点续传的关键几点
(1)用什么方法实现提交range: bytes=2000070-。
当然用最原始的socket是肯定能完成的,不过那样太费事了,其实java的net包中提供了这种功能。代码如下:
url url = new url("http://www.sjtu.edu.cn/down.zip");
httpurlconnection httpconnection = (httpurlconnection)url.openconnection();
//设置user-agent
httpconnection.setrequestproperty("user-agent","netfox");
//设置断点续传的开始位置
httpconnection.setrequestproperty("range","bytes=2000070");
//获得输入流
inputstream input = httpconnection.getinputstream();
从输入流中取出的字节流就是down.zip文件从2000070开始的字节流。
大家看,其实断点续传用java实现起来还是很简单的吧。
接下来要做的事就是怎么保存获得的流到文件中去了。
保存文件采用的方法。
我采用的是io包中的randaccessfile类。
操作相当简单,假设从2000070处开始保存文件,代码如下:
randomaccess osavedfile = new randomaccessfile("down.zip","rw");
long npos = 2000070;
//定位文件指针到npos位置
osavedfile.seek(npos);
byte[] b = new byte[1024];
int nread;
//从输入流中读入字节流,然后写到文件中
while((nread=input.read(b,0,1024)) > 0)
{
osavedfile.write(b,0,nread);
}
怎么样,也很简单吧。
接下来要做的就是整合成一个完整的程序了。包括一系列的线程控制等等。
部分程序原码:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.tree.*;
import javax.swing.border.*;
import java.net.*;
import java.util.*;
import java.io.*;
public class testants extends jframe{
private jmenu file,edit,download,view,option,tool,help;
private jmenuitem exit,add,preference,bookmark,helptopic,about;
private static jmenuitem start,stop;
private jcheckboxmenuitem istoolbar,isstatusbar;
private jmenubar menubar;
private jtoolbar toolbar;
private jbutton addbut,preferencebut,helpbut,exitbut;
private static jbutton startbut,stopbut;
private jsplitpane leftsp;
private jcomponent left,right;
private jlabel statusbar,label,lurl,lpath,lname,lnum;
public static jtextfield turl,tpath,tname,tnum;
private jpanel panel;
private defaulttreecellrenderer render;
private jscrollpane scrollpane;
private jtree tree;
public static jprogressbar pb;
private listener listener;
private container contentpane;
public testants(string name){
super(name);
init();
}
public void init(){
listener = new listener();
//toolbar component add
startbut = new jbutton("start");
startbut.setenabled(false);
startbut.settooltiptext("start");
startbut.addactionlistener(listener);
stopbut = new jbutton("stop");
stopbut.setenabled(false);
stopbut.settooltiptext("stop");
stopbut.addactionlistener(listener);
addbut = new jbutton("add...");
addbut.settooltiptext("add...");
addbut.addactionlistener(listener);
preferencebut = new jbutton("preference...");
preferencebut.settooltiptext("preference...");
preferencebut.addactionlistener(listener);
helpbut = new jbutton("help");
helpbut.settooltiptext("help");
helpbut.addactionlistener(listener);
exitbut = new jbutton("exit");
exitbut.settooltiptext("exit");
exitbut.addactionlistener(listener);
toolbar = new jtoolbar();
toolbar.add(startbut);
toolbar.add(stopbut);
toolbar.add(addbut);
toolbar.add(preferencebut);
toolbar.addseparator();
toolbar.add(helpbut);
toolbar.addseparator();
toolbar.add(exitbut);
//leftsp component
leftsp = new jsplitpane();
leftsp.setcontinuouslayout(false);
leftsp.setdividersize(10);
leftsp.setdividerlocation(204);
left = (jcomponent)leftsp.getleftcomponent();
right = (jcomponent)leftsp.getrightcomponent();
left.setbackground(color.green);
right.setbackground(color.green);
pb = new jprogressbar(0,100);
pb.setforeground(color.red);
label = new jlabel("progressmonitor..");
lurl = new jlabel("download url:");
lpath = new jlabel("save to:");
lname = new jlabel("rename as:");
lnum = new jlabel("progress num:");
turl = new jtextfield(20);
turl.setenabled(false);
tpath = new jtextfield(20);
tpath.setenabled(false);
tname = new jtextfield(20);
tname.setenabled(false);
tnum = new jtextfield(20);
tnum.setenabled(false);
label.setforeground(color.blue);
panel = new jpanel();
panel.setbackground(color.green);
panel.add(label);
panel.add(pb);
panel.add(lurl);
panel.add(turl);
panel.add(lpath);
panel.add(tpath);
panel.add(lname);
panel.add(tname);
panel.add(lnum);
panel.add(tnum);
right.add(panel,borderlayout.north);
render = new defaulttreecellrenderer();
render.setbackgroundnonselectioncolor(color.green);
render.settextnonselectioncolor(color.blue);
render.settextselectioncolor(color.red);
tree = new jtree(createnodes());
tree.setcellrenderer(render);
tree.setbackground(color.green);
scrollpane = new jscrollpane(tree);
left.add(scrollpane);
//statusbar
statusbar = new jlabel("statusbar");
//menubar
file = new jmenu("file");
file.setmnemonic('f');
exit = new jmenuitem("exit");
exit.setmnemonic('x');
exit.addactionlistener(listener);
file.add(exit);
edit = new jmenu("edit");
edit.setmnemonic('e');
add = new jmenuitem("add...");
add.setmnemonic('a');
add.addactionlistener(listener);
edit.add(add);
download = new jmenu("download");
download.setmnemonic('d');
start = new jmenuitem("start");
start.setenabled(false);
start.addactionlistener(listener);
stop = new jmenuitem("stop");
stop.setenabled(false);
stop.addactionlistener(listener);
download.add(start);
download.add(stop);
view = new jmenu("view");
view.setmnemonic('v');
istoolbar = new jcheckboxmenuitem("toolbar");
isstatusbar = new jcheckboxmenuitem("statusbar");
view.add(istoolbar);
view.add(isstatusbar);
option = new jmenu("option");
option.setmnemonic('o');
preference = new jmenuitem("preference..");
preference.addactionlistener(listener);
option.add(preference);
tool = new jmenu("tool");
tool.setmnemonic('t');
bookmark = new jmenuitem("bookmark..");
bookmark.addactionlistener(listener);
tool.add(bookmark);
help = new jmenu("help");
help.setmnemonic('h');
helptopic = new jmenuitem("help topic...");
helptopic.addactionlistener(listener);
about = new jmenuitem("about...");
about.addactionlistener(listener);
help.add(helptopic);
help.add(about);
menubar = new jmenubar();
menubar.add(file);
menubar.add(edit);
menubar.add(download);
menubar.add(view);
menubar.add(option);
menubar.add(tool);
menubar.add(help);
//add all
contentpane = getcontentpane();
contentpane.add(toolbar,borderlayout.north);
contentpane.add(leftsp,borderlayout.center);
contentpane.add(statusbar,borderlayout.south);
setjmenubar(menubar);
}
public defaultmutabletreenode createnodes(){
defaultmutabletreenode rootnode = new defaultmutabletreenode("virtual folder");
defaultmutabletreenode missionstate=new defaultmutabletreenode("mission state");
defaultmutabletreenode downdate=new defaultmutabletreenode("down date");
defaultmutabletreenode today=new defaultmutabletreenode("today");
defaultmutabletreenode yesterday=new defaultmutabletreenode("yesterday");
defaultmutabletreenode lastweek=new defaultmutabletreenode("last week");
defaultmutabletreenode lastweek3=new defaultmutabletreenode("last week3");
defaultmutabletreenode normal=new defaultmutabletreenode("normal");
defaultmutabletreenode running=new defaultmutabletreenode("running");
rootnode.add(missionstate);
rootnode.add(downdate);
downdate.add(today);
downdate.add(yesterday);
downdate.add(lastweek);
missionstate.add(normal);
missionstate.add(running);
return rootnode;
}
public static void main(string[] args) {
testants ta = new testants("网络蚂蚁ant");
ta.setsize(600,410);
ta.setdefaultcloseoperation(jframe.exit_on_close);
ta.show();
}
public static void setstarttrue(){
startbut.setenabled(true);
start.setenabled(true);
}
public static void setstartfalse(){
startbut.setenabled(false);
start.setenabled(false);
}
public static void setstoptrue(){
startbut.setenabled(true);
start.setenabled(true);
}
public static void setstopfalse(){
startbut.setenabled(false);
start.setenabled(false);
}
public void showadddialog(){
adddialog ad =new adddialog(this);
ad.setsize(380,340);
ad.setmodal(true);
ad.center();
ad.setdefaultcloseoperation(windowconstants.dispose_on_close);
ad.show();
}
class listener implements actionlistener{
public void actionperformed(actionevent e){
string arg=e.getactioncommand();
if(arg=="exit"){
system.exit(0);
}else if(arg=="add..."){
showadddialog();//
}else if(arg=="start"){
splitfile sf = new splitfile();
sf.start();
}
}
}
}
如果想获得java版ant的原码:http://www.fls-cts.com/kkjvk/
闽公网安备 35060202000074号