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对象。
using System.IO; // 流支持
using System.Threading ; // 线程支持
增加如下的程序变量:
public bool [] threadw; // 每个线程结束标志
public string [] filenamew; // 每个线程接收文件的文件名
public int [] filestartw; // 每个线程接收文件的起始位置
public int [] filesizew; // 每个线程接收文件的大小
public string strurl; // 接受文件的URL
public bool hb; // 文件合并标志
public int thread; // 进程数
定义一个HttpFile类,用于管理接收线程,其代码如下:
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 ;
}
}
该类和Form1类处于统一命名空间,但不包含在Form1类中。下面定义“开始接收”按钮控件的事件响应函数:
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 ();
}
合并文件的线程hbfile定义在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 ( " 接收完毕!!! " );
}