java 实现多线程下载(1)

来源:互联网 发布:php从入门到精通韩顺平 编辑:程序博客网 时间:2024/06/14 09:42

网络蚂蚁、flashget、迅雷等支持HTTP协议的下载软件无一例外地使用了多线程下载技术。比起单线程下载,多线程下载在同一时间段内发出多个下载请求,每个下载请求负责下载一段内存,充分地利用了网络带宽。当然多线程下载并非线程数越多越好。试想,一个极端的情况:一个尺寸为1024个字节的远程文件,动用1024个线程来下载,每个线程平均只下载一个字节,创建线程的代价和对自身网络出口造成的堵塞远远大于分工下载带来的好处。因此,多线程下载存在一个权衡的问题。一般来说,需要事先根据待下载的远程文件的尺寸来决定启用多少个线程,如果文件很小,则意味着使用单线程下载即可。而且,下载软件允许创建的线程数一般是设置上限的,例如即使下载一个超大文件,也不能开启过多的线程,毕竟创建下载线程是需要耗费客户端资源的,并且线程之间存在着竞争网络带宽的关系。

总之,下载线程数往往是待下载的远程文件尺寸、每个线程分担的字节数任务、线程数三者之间权衡的结果。

实战多线程下载,有几个技术难题有待攻克:

•如何获取远程文件的尺寸,这关系到开启多少个下载线程。下面的例子采用比较简单的线程数决策策略:固定每个线程分担的字节数任务,根据远程文件尺寸来决定需要开启的下载线程,也就是不考虑下载线程数过多带来的负面影响。因此,获取远程文件的尺寸就成为了很关键的一个步骤。

•如何实现分工下载,即每个线程只下载远程文件的一段。这是多线程HTTP下载的核心技术。

•如何存储、组织各个线程下载得到的文件碎片,最后将其拼成一个完整的文件。

首先来解决第一个问题:如何获取远程文件的尺寸。我们知道,在HTTP反馈报文的头部分(Header)有一些数据项,其中有一项便是Content-Length,表示的便是Http反馈报文的正文部分的字节数。我们经常以Post、Get等方式发起HTTP请求,实际上HTTP协议还指出以Head方式发出请求。head方式发出的HTTP请求,表示仅需要HTTP服务器返回头部分、无须返回正文。解读了如何请求报文头的问题之后,在Java程序总如getHeaderFieldKey(int n)和getHeaderField(int n)方法读取HTTP反馈报文头,其中getHeaderFieldKey(int n)返回的是第n个数据项的名称,getHeaderField(int n)返回的是第n个数据项的值。之所以只支持根据数据项编号来获取数据项值,是因为不同的服务器(Apache和IIS)在返回反馈报文时,报文头中的数据项顺序是不一致的,而HTTP协议本身也并没有规定标准数据项的顺序。

接下来解决第二个问题:如何实现分工下载,即每个线程只下载文件的一段。HTTP请求报文头有一个不经常为人使用的数据项:Range,它代表的是下载的字节范围,如0-1024代表从文件开始处下载到地1024个字节数。一般情况下,Web浏览器在下载远程文件时都不适用这个数据项,这就代表无论多大的文件,浏览器都试图用一次HTTP骑牛来下载。

那么最后一个问题在上面的日志中提到过了,与文件的分割和合并是一个道理,使用随机文件存储技术,边下载边填充。

关于代码的具体实现请看下篇日志!!