查了下资料,Http断点续传主要是Http请求包中的Range头
多线程下载需要管理好每一个线程下载的文件段
整个代码大致如下
-
using System;
-
using ;
-
using ;
-
using ;
-
using ;
-
using ;
-
using ;
-
using ;
-
using ;
-
-
namespace FengBan
-
{
-
-
-
///
-
/// 多线程下载、断点下载的管理类
-
///
-
class DFGManager : IDisposable
-
{
-
private string dfgFilename;
-
private int size;
-
private FileStream writer;
-
private SortedSet
-
-
-
-
rset = new SortedSet
-
-
-
-
(new RangeCompare());
-
-
///
-
-
-
/// 多线程下载、断点下载的管理类
-
///
-
///
-
-
-
internal DFGManager(string dfgFilename)
-
{
-
this.dfgFilename = dfgFilename;
-
if ((dfgFilename))
-
{
-
ReadDFGFile();
-
(dfgFilename);
-
}
-
-
writer = new FileStream(dfgFilename, );
-
WriteDFGFile();
-
-
}
-
-
///
-
-
-
/// 获取或设置要下载的文件大小
-
///
-
public int Size
-
{
-
get { return size; }
-
set
-
{
-
size = value;
-
byte[] head = new byte[16];
-
byte[] bsize = (size);
-
int i;
-
for (i = 0; i < (); i++)
-
{
-
head[i] = bsize[i];
-
}
-
(0, );
-
(head, 0, 16);
-
();
-
}
-
}
-
-
///
-
-
-
/// 释放文件段,该段已经下载完成
-
///
-
///
-
-
-
public void Free(Range range)
-
{
-
lock (rset)
-
{
-
var find = from r in rset
-
where == && == 1
-
select r;
-
if (() == 1)
-
{
-
var ele = ();
-
= 2;
-
= 2;
-
if ( < )
-
{
-
= ;
-
}
-
else
-
{
-
= ;
-
}
-
MergeRange();
-
((), 0, 4);
-
((), 0, 4);
-
();
-
}
-
else if (() == 0)
-
{
-
throw new Exception("找不到释放的块");
-
}
-
else
-
{
-
throw new Exception("多个符合条件的块");
-
}
-
}
-
}
-
-
///
-
-
-
/// 分配文件段
-
///
-
///
-
-
期望分配的大小
-
///
-
-
-
-
实际分配的大小
-
-
-
public Range Alloc(int expectSize = 102400)
-
{
-
lock (rset)
-
{
-
Range range = new Range();
-
if ( == 0)
-
{
-
= 0;
-
= (expectSize, this.size);
-
= 1;
-
(range);
-
}
-
else if ( == 1)
-
{
-
if (().left > 0)
-
{
-
= 0;
-
= ().left;
-
= 1;
-
(range);
-
}
-
else if (().right < size)
-
{
-
= ().right;
-
= (size, + expectSize);
-
= 1;
-
(range);
-
}
-
else
-
{
-
range = null;
-
}
-
}
-
else
-
{
-
if (().left > 0)
-
{
-
= 0;
-
= ().left;
-
= 1;
-
(range);
-
}
-
else if (().right < size)
-
{
-
= ().right;
-
= (size, + expectSize);
-
= 1;
-
(range);
-
}
-
else
-
{
-
for (int i = 1; i < ; i++)
-
{
-
Range l = (i - 1);
-
Range r = (i);
-
if ( < )
-
{
-
= ;
-
= ;
-
= 1;
-
(range);
-
return range;
-
}
-
}
-
range = null;
-
}
-
}
-
return range;
-
}
-
-
}
-
-
private void ReadDFGFile()
-
{
-
-
StreamReader reader = new StreamReader(dfgFilename);
-
byte[] head = new byte[16];/*head占4个int*/
-
(head, 0, 16);
-
-
size = BitConverter.ToInt32(head, 0);
-
-
byte[] buff = new byte[8];
-
int rd = 0;
-
do
-
{
-
rd = (buff, 0, 8);
-
if (rd == 0)
-
{
-
break;
-
}
-
-
Range range = new Range();
-
= BitConverter.ToInt32(buff, 0);
-
= BitConverter.ToInt32(buff, 4);
-
= 2;
-
-
(range);
-
-
}
-
while (true);
-
-
();
-
-
MergeRange();
-
}
-
-
private void WriteDFGFile()
-
{
-
-
-
byte[] head = new byte[16];
-
byte[] bsize = (size);
-
int i;
-
for (i = 0; i < (); i++)
-
{
-
head[i] = bsize[i];
-
}
-
(head, 0, 16);
-
for (i = 0; i < ; i++)
-
{
-
Range r = (i);
-
if ( == 2)
-
{
-
((), 0, 4);
-
((), 0, 4);
-
}
-
}
-
();
-
}
-
-
private void MergeRange()
-
{
-
int i;
-
if ( >= 2)
-
{
-
Range last = ();
-
for (i = 1; i < && >= 2; i++)
-
{
-
Range cur = (i);
-
if ( <= && == 2 && == 2)
-
{
-
= ;
-
(cur);
-
i--;
-
}
-
else
-
{
-
last = cur;
-
}
-
}
-
}
-
-
}
-
-
///
-
-
-
/// 释放资源
-
///
-
public void Dispose()
-
{
-
if (writer != null)
-
{
-
();
-
writer = null;
-
}
-
}
-
-
~DFGManager()
-
{
-
Dispose();
-
}
-
-
}
-
-
class Range
-
{
-
public int left;
-
public int right;
-
public int status;
-
}
-
-
class RangeCompare : IComparer
-
-
-
-
-
{
-
-
public int Compare(Range x, Range y)
-
{
-
return ( - );
-
}
-
}
-
-
}
-
using System;
-
using ;
-
using ;
-
using ;
-
using ;
-
using ;
-
using ;
-
using ;
-
using ;
-
using ;
-
-
namespace FengBan
-
{
-
class Program
-
{
-
static void Main()
-
{
-
MultiThreadDownload downloader = new MultiThreadDownload();
-
-
("/sw-search-sp/soft/3a/12350/QQ_V7.0.14275.0_setup.", "");
-
}
-
}
-
-
///
-
-
-
/// 多线程下载类
-
///
-
class MultiThreadDownload
-
{
-
/*dfg文件记录下载好的文件段*/
-
private const string suffix = ".dfg";
-
-
private string site;/*文件下载地址*/
-
private string outname;/*下载的文件名称*/
-
-
private DFGManager dfgManager;/*文件分段下载管理对象*/
-
private FileStream writer;/*多个下载子线程共用一个输出流*/
-
-
/*通过currentDownload和lastDownload来实现限速*/
-
private int currentDownload;
-
private int lastDownload;
-
private int shouldSleep;/*限速时应该休眠的时长,单位毫秒*/
-
private timer;/*限速计时器*/
-
-
/*上网代理*/
-
public string ProxyAddressAndPort { get; set; }
-
public string ProxyUserName { get; set; }
-
public string ProxyPassword { get; set; }
-
-
public MultiThreadDownload(int ThreadCount = 5)
-
{
-
this.ThreadCount = ThreadCount;
-
-
timer = new (1000);
-
+= CheckDownloadSpeed;
-
= true;
-
}
-
-
///
-
-
-
/// 下载文件的函数,仅支持http协议下载
-
///
-
///
-
-
文件下载地址
-
///
-
-
输出文件名
-
public void Download(string site, string outname)
-
{
-
string realname = outname;
-
outname += ".down";
-
this.site = site;
-
this.outname = outname;
-
-
try
-
{
-
/*根据需要设置代理*/
-
if (ProxyEnable)
-
{
-
ICredentials cred = new NetworkCredential(ProxyUserName, ProxyPassword);
-
WebProxy p = new WebProxy(ProxyAddressAndPort, true, null, cred);
-
= p;
-
}
-
-
/*获取要下载的文件的信息,主要是获取文件大小*/
-
HttpWebRequest request = (HttpWebRequest)(site);
-
= "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36";
-
-
WebResponse response = ();
-
Stream responseStream = ();
-
long contentLength = ;
-
("要下载的文件大小:{0}", contentLength);
-
-
();
-
();
-
-
/*创建管理对象,负责给每一个线程分配下载的文件段*/
-
dfgManager = new DFGManager(outname + suffix);
-
= Convert.ToInt32(contentLength);
-
-
currentDownload = 0;
-
lastDownload = 0;
-
writer = new FileStream(outname, );
-
ManualResetEvent[] _ManualEvents = new ManualResetEvent[ThreadCount];
-
//开启线程池下载
-
for (int i = 0; i < ThreadCount; i++)
-
{
-
_ManualEvents[i] = new ManualResetEvent(false);
-
(new WaitCallback(SDown), _ManualEvents[i]);
-
}
-
(_ManualEvents);/*等待子线程下载结束*/
-
("全部下载子线程结束");
-
-
//下载完成,释放资源
-
();
-
();
-
-
/*收尾*/
-
if ((realname))
-
{
-
(realname);
-
}
-
(outname, realname);
-
(outname + suffix);
-
}
-
finally
-
{
-
-
}
-
}
-
-
-
-
private void CheckDownloadSpeed(object sender, e)
-
{
-
int curSpeed = (currentDownload - lastDownload);
-
("当前速度 {0:f} kb/s", ((double)curSpeed) / 1000);
-
lastDownload = currentDownload;
-
-
//如果当前速度超过设定的速度,则应该执行限速动作
-
-
//未实现
-
}
-
-
///
-
-
-
/// 设置代理
-
///
-
///
-
-
代理服务器地址和端口
-
///
-
-
代理服务器用户名
-
///
-
-
代理服务器密码
-
public void SetProxy(string ProxyAddressAndPort, string Username, string Password)
-
{
-
this.ProxyAddressAndPort = ProxyAddressAndPort;
-
this.ProxyUserName = Username;
-
this.ProxyPassword = Password;
-
this.ProxyEnable = true;
-
}
-
-
/*是否使用代理*/
-
public bool ProxyEnable
-
{
-
get;
-
set;
-
}
-
-
///
-
-
-
/// 设置或获取多线程下载的数量
-
///
-
public int ThreadCount
-
{
-
set;
-
get;
-
}
-
-
/*多线程下载的线程函数*/
-
private void SDown(object param)
-
{
-
-
Range range;
-
byte[] buff = null;
-
-
/*从dfgManager中获取要下载的文件段*/
-
while ((range = ()) != null)
-
{
-
-
HttpWebRequest request = (HttpWebRequest)(site);
-
(, );
-
= "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36";
-
-
WebResponse response = ();
-
int contentLength = (int);
-
-
//分配下载的缓存空间
-
if (buff == null || () < contentLength)
-
{
-
buff = new byte[contentLength];
-
}
-
-
//使用while循环读取全部字节到buff中
-
int count = 0;
-
while (count < contentLength)
-
{
-
count += ().Read(buff, count, (int)contentLength - count);
-
}
-
-
//真正从服务器下载下来的文件段
-
Range realRange = new Range();
-
= ;
-
= ( + count, );
-
(realRange);
-
-
();
-
-
//将buff写入文件
-
(, );
-
(buff, 0, - );
-
();
-
-
int realDown = - ;
-
currentDownload += realDown;
-
("本次分段下载大小{0}", realDown);
-
-
if (LimitSpeed > 0 && shouldSleep > 10)
-
{
-
("休眠{0}", shouldSleep);
-
(shouldSleep);
-
}
-
}
-
-
("线程执行结束");
-
/*通知主线程执行完成*/
-
ManualResetEvent e = (ManualResetEvent)param;
-
();
-
}
-
-
private int xiansu;
-
///
-
-
-
/// 表示是否限速,0表示不限速,单位 b/s
-
///
-
public int LimitSpeed
-
{
-
get
-
{
-
return xiansu;
-
}
-
set
-
{
-
xiansu = value;
-
if (value > 0)
-
{
-
= true;
-
}
-
}
-
}
-
}
-
}
-
-
-
-
-
-