多线程下载
很多人都有过使用网络蚂蚁或网络快车软件下载互联网文件的经历,这些软件的使用可以大大加速互联网上文件的传输速度,减少文件传输的时间。这些软件为什么
有如此大的魔力呢?其主要原因是这些软件都采用了多线程下载和断点续传技术。如果我们自己来编写一个类似这样的程序,也能够快速的在互联网上下载文件,那
一定是非常愉快的事情。下面我就讲一讲如何利用C#语言编写一个支持多线程下载文件的程序,你会看到利用C#语言编写网络应程序是多么的容易,从中也能体
会到C#语言中强大的网络功能。
首先介绍一下HTTP协议,HTTP亦即Hpyer Text Transfer Protocal的缩写,它是现代互联网上最重要的一种网络协议,超文本传输协议位于TCP/IP协议的应用层,是一个面向无连接、简单、快速的C/S结 构的协议。HTTP的工作过程大体上分连接、请求、响应和断开连接四个步骤。C#语言对HTTP协议提供了良好的支持,在.NET类库中提供了 WebRequest和WebResponse类,这两个类都包含在System.Net命名空间中,利用这两个类可以实现很多高级的网络功能,本文中多 线程文件下载就是利用这两个类实现的。 WebRequest和WebResponse都是抽象基类,因此在程序中不能直接作为对象使用,必须被继承,实际使用中,可根据URI参数中的URI前 缀选用它们合适的子类,对于HTTP这类URI,HttpWebRequest和HttpWebResponse类可以用于处理客户程序同WEB服务器之 间的HTTP通讯。
HttpWebRequest类实现了很多通过HTTP访问WEB服务器上文件的高级功能。HttpWebRequest类对WebRequest中定义 的属性和方法提供支持,HttpWebRequest将发送到Internet资源的公共HTTP标头的值公开为属性,由方法或系统设置,常用的由属性或 方法设置的HTTP标头为:接受, 由Accept属性设置, 连接, 由Connection属性和KeepAlive属性设置, Content-Length, 由ContentLength属性设置, Content-Type, 由ContentType属性设置, 范围, 由AddRange方法设置. 实际使用中是将标头信息正确设置后,传递到WEB服务器,WEB服务器根据要求作出回应。
HttpWebResponse类继承自WebResponse类,专门处理从WEB服务器返回的HTTP响应,这个类实现了很多方法,具有很多属性,可 以全面处理接收到的互联网信息。在HttpWebResponse类中,对于大多数通用的HTTP标头字段,都有独立的属性与其对应,程序员可以通过这些 属性方便的访问位于HTTP接收报文标头字段中的信息,本例中用到的HttpWebResponse类属性为:ContentLength 既接收内容的长度。
有了以上的了解后,下面看看这两个类的用法,要创建HttpWebRequest对象,不要直接使用HttpWebRequest的构造函数,而要使用WebRequest.Create方法初始化一个HttpWebRequest实例,如:
HttpWebReques对象使用GetResponse()方法时,会返回一个HttpWebResponse对象,为提出HTTP返回报文信息,需 要使用HttpWebResponse的GetResponseStream()方法,该方法返回一个Stream对象,可以读取HTTP返回的报文, 如:首先定义一个Strean 对象 public System.IO.Stream ns; 然后 ns=hwr.GetResponse ().GetResponseStream ();即可创建Stream对象。 控件定义代码是:
定义一个HttpFile类,用于管理接收线程,其代码如下:
首先介绍一下HTTP协议,HTTP亦即Hpyer Text Transfer Protocal的缩写,它是现代互联网上最重要的一种网络协议,超文本传输协议位于TCP/IP协议的应用层,是一个面向无连接、简单、快速的C/S结 构的协议。HTTP的工作过程大体上分连接、请求、响应和断开连接四个步骤。C#语言对HTTP协议提供了良好的支持,在.NET类库中提供了 WebRequest和WebResponse类,这两个类都包含在System.Net命名空间中,利用这两个类可以实现很多高级的网络功能,本文中多 线程文件下载就是利用这两个类实现的。 WebRequest和WebResponse都是抽象基类,因此在程序中不能直接作为对象使用,必须被继承,实际使用中,可根据URI参数中的URI前 缀选用它们合适的子类,对于HTTP这类URI,HttpWebRequest和HttpWebResponse类可以用于处理客户程序同WEB服务器之 间的HTTP通讯。
HttpWebRequest类实现了很多通过HTTP访问WEB服务器上文件的高级功能。HttpWebRequest类对WebRequest中定义 的属性和方法提供支持,HttpWebRequest将发送到Internet资源的公共HTTP标头的值公开为属性,由方法或系统设置,常用的由属性或 方法设置的HTTP标头为:接受, 由Accept属性设置, 连接, 由Connection属性和KeepAlive属性设置, Content-Length, 由ContentLength属性设置, Content-Type, 由ContentType属性设置, 范围, 由AddRange方法设置. 实际使用中是将标头信息正确设置后,传递到WEB服务器,WEB服务器根据要求作出回应。
HttpWebResponse类继承自WebResponse类,专门处理从WEB服务器返回的HTTP响应,这个类实现了很多方法,具有很多属性,可 以全面处理接收到的互联网信息。在HttpWebResponse类中,对于大多数通用的HTTP标头字段,都有独立的属性与其对应,程序员可以通过这些 属性方便的访问位于HTTP接收报文标头字段中的信息,本例中用到的HttpWebResponse类属性为:ContentLength 既接收内容的长度。
有了以上的了解后,下面看看这两个类的用法,要创建HttpWebRequest对象,不要直接使用HttpWebRequest的构造函数,而要使用WebRequest.Create方法初始化一个HttpWebRequest实例,如:
HttpWebRequest hwr=(HttpWebRequest)WebRequest.Create(http://www.163.com/);创建了这个对象后,就可以通过HttpWebRequest属性,设置很多HTTP标头字段的内容,如hwr.AddRange(100,1000);设置接收对象的范围为100-1000字节。
HttpWebReques对象使用GetResponse()方法时,会返回一个HttpWebResponse对象,为提出HTTP返回报文信息,需 要使用HttpWebResponse的GetResponseStream()方法,该方法返回一个Stream对象,可以读取HTTP返回的报文, 如:首先定义一个Strean 对象 public System.IO.Stream ns; 然后 ns=hwr.GetResponse ().GetResponseStream ();即可创建Stream对象。 控件定义代码是:
public System.Windows.Forms.ListBox listBox1;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.TextBox textBox1
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.TextBox textBox2;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.TextBox textBox3;
private System.Windows.Forms.Label label4;
private System.Windows.Forms.TextBox textBox4;
打开Form1的代码编辑器,增加如下的命名空间:
using System.Net;//网络功能 using System.IO;//流支持 using System.Threading ;//线程支持
增加如下的程序变量:
合并文件的线程hbfile定义在Form1类中,定义如下:public bool[] threadw; //每个线程结束标志 public string[] filenamew;//每个线程接收文件的文件名 public int[] filestartw;//每个线程接收文件的起始位置 public int[] filesizew;//每个线程接收文件的大小 public string strurl;//接受文件的URL public bool hb;//文件合并标志 public int thread;//进程数
该类和Form1类处于统一命名空间,但不包含在Form1类中。下面定义“开始接收”按钮控件的事件响应函数:public void hbfile()
{
while (true)//等待 {
hb=true;
for (int i=0;i<thread;i++)
{
if (threadw[i]==false)//有未结束线程,等待 {
hb=false;
Thread.Sleep (100);
break;
} } if (hb==true)//所有线程均已结束,停止等待, {
break;
} } FileStream fs;//开始合并 FileStream fstemp;
int readfile;
byte[] bytes=new byte[512];
fs=new FileStream (textBox3.Text .Trim ().ToString (),System.IO.FileMode.Create);
for (int k=0;k<thread;k++)
{
fstemp=new FileStream (filenamew[k],System.IO.FileMode .Open);
while (true)
{
readfile=fstemp.Read (bytes,0,512);
if (readfile>0)
{
fs.Write (bytes,0,readfile);
} else {
break;
} } fstemp.Close ();
} fs.Close ();
DateTime dt=DateTime.Now;
textBox1.Text =dt.ToString ();//结束时间 MessageBox.Show ("接收完毕!!!");
}
private void button1_Click(object sender, System.EventArgs e)
{
DateTime dt=DateTime.Now;//开始接收时间 textBox1.Text =dt.ToString ();
strurl=textBox2.Text .Trim ().ToString ();
HttpWebRequest request;
long filesize=0;
try {
request=(HttpWebRequest)HttpWebRequest.Create (strurl);
filesize=request.GetResponse ().ContentLength;//取得目标文件的长度 request.Abort ();
} catch (Exception er)
{
MessageBox.Show (er.Message );
} // 接收线程数 thread=Convert.ToInt32 (textBox4.Text .Trim().ToString (),10);
//根据线程数初始化数组 threadw=new bool [thread];
filenamew=new string [thread];
filestartw=new int [thread];
filesizew=new int[thread];
//计算每个线程应该接收文件的大小 int filethread=(int)filesize/thread;//平均分配 int filethreade=filethread+(int)filesize%thread;//剩余部分由最后一个线程完成
//为数组赋值 for (int i=0;i<thread;i++)
{
threadw[i]=false;//每个线程状态的初始值为假 filenamew[i]=i.ToString ()+".dat";//每个线程接收文件的临时文件名 if (i<thread-1)
{
filestartw[i]=filethread*i;//每个线程接收文件的起始点 filesizew[i]=filethread-1;//每个线程接收文件的长度 } else {
filestartw[i]=filethread*i;
filesizew[i]=filethreade-1;
} } //定义线程数组,启动接收线程 Thread[] threadk=new Thread [thread];
HttpFile[] httpfile=new HttpFile [thread];
for (int j=0;j<thread;j++)
{
httpfile[j]=new HttpFile(this,j);
threadk[j]=new Thread(new ThreadStart (httpfile[j].receive ));
threadk[j].Start ();
} //启动合并各线程接收的文件线程 Thread hbth=new Thread (new ThreadStart (hbfile));
hbth.Start ();
}
public class HttpFile
{
public Form1 formm;
public int threadh;//线程代号 public string filename;//文件名 public string strUrl;//接收文件的URL public FileStream fs;
public HttpWebRequest request;
public System.IO.Stream ns;
public byte[] nbytes;//接收缓冲区 public int nreadsize;//接收字节数 public HttpFile(Form1 form,int thread)//构造方法 {
formm=form;
threadh=thread;
} ~HttpFile()//析构方法 {
formm.Dispose ();
} public void receive()//接收线程 {
filename=formm.filenamew[threadh];
strUrl=formm.strurl;
ns=null;
nbytes= new byte[512];
nreadsize=0;
formm.listBox1 .Items .Add ("线程"+threadh.ToString ()+"开始接收");
fs=new FileStream (filename,System.IO.FileMode.Create);
try {
request=(HttpWebRequest)HttpWebRequest.Create (strUrl);
//接收的起始位置及接收的长度 request.AddRange(formm.filestartw [threadh],
formm.filestartw [threadh]+formm.filesizew [threadh]);
ns=request.GetResponse ().GetResponseStream ();//获得接收流 nreadsize=ns.Read (nbytes,0,512);
while (nreadsize>0)
{
fs.Write (nbytes,0,nreadsize);
nreadsize=ns.Read (nbytes,0,512);
formm.listBox1 .Items .Add ("线程"+threadh.ToString ()+"正在接收");
} fs.Close();
ns.Close ();
} catch (Exception er)
{
MessageBox.Show (er.Message );
fs.Close();
} formm.listBox1 .Items.Add ("进程"+threadh.ToString ()+"接收完毕!");
formm.threadw[threadh]=true;
} }