轉(zhuǎn)帖|其它|編輯:郝浩|2011-06-07 14:02:27.000|閱讀 688 次
概述:本文講如何利用C#語言編寫一個(gè)支持多線程下載文件的程序,你會(huì)看到利用C#語言編寫網(wǎng)絡(luò)應(yīng)程序是多么的容易,從中也能體會(huì)到C#語言中強(qiáng)大的網(wǎng)絡(luò)功能。
# 界面/圖表報(bào)表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
很多人都有過使用網(wǎng)絡(luò)螞蟻或網(wǎng)絡(luò)快車軟件下載互聯(lián)網(wǎng)文件的經(jīng)歷,這些軟件的使用可以大大加速互聯(lián)網(wǎng)上文件的傳輸速度,減少文件傳輸?shù)臅r(shí)間。這些軟件為什么有如此大的魔力呢?其主要原因是這些軟件都采用了多線程下載和斷點(diǎn)續(xù)傳技術(shù)。如果我們自己來編寫一個(gè)類似這樣的程序,也能夠快速的在互聯(lián)網(wǎng)上下載文件,那一定是非常愉快的事情。下面我就講一講如何利用C#語言編寫一個(gè)支持多線程下載文件的程序,你會(huì)看到利用C#語言編寫網(wǎng)絡(luò)應(yīng)程序是多么的容易,從中也能體會(huì)到C#語言中強(qiáng)大的網(wǎng)絡(luò)功能。
首先介紹一下HTTP協(xié)議,HTTP亦即Hpyer Text Transfer Protocal的縮寫,它是現(xiàn)代互聯(lián)網(wǎng)上最重要的一種網(wǎng)絡(luò)協(xié)議,超文本傳輸協(xié)議位于TCP/IP協(xié)議的應(yīng)用層,是一個(gè)面向無連接、簡單、快速的C/S結(jié)構(gòu)的協(xié)議。HTTP的工作過程大體上分連接、請(qǐng)求、響應(yīng)和斷開連接四個(gè)步驟。
C#語言對(duì)HTTP協(xié)議提供了良好的支持,在.NET類庫中提供了WebRequest和WebResponse類,這兩個(gè)類都包含在System.Net命名空間中,利用這兩個(gè)類可以實(shí)現(xiàn)很多高級(jí)的網(wǎng)絡(luò)功能,本文中多線程文件下載就是利用這兩個(gè)類實(shí)現(xiàn)的。 WebRequest和WebResponse都是抽象基類,因此在程序中不能直接作為對(duì)象使用,必須被繼承,實(shí)際使用中,可根據(jù)URI參數(shù)中的URI前綴選用它們合適的子類,對(duì)于HTTP這類URI, HttpWebRequest和HttpWebResponse類可以用于處理客戶程序同WEB服務(wù)器之間的HTTP通訊。
HttpWebRequest類實(shí)現(xiàn)了很多通過HTTP訪問WEB服務(wù)器上文件的高級(jí)功能。HttpWebRequest類對(duì)WebRequest中定義的屬性和方法提供支持,HttpWebRequest將發(fā)送到Internet資源的公共HTTP標(biāo)頭的值公開為屬性,由方法或系統(tǒng)設(shè)置,常用的由屬性或方法設(shè)置的HTTP標(biāo)頭為:接受--由Accept屬性設(shè)置;連接 -- 由Connection屬性和KeepAlive屬性設(shè)置; Content-Length -- 由ContentLength屬性設(shè)置;Content-Type -- 由ContentType屬性設(shè)置;范圍 -- 由AddRange方法設(shè)置。實(shí)際使用中是將標(biāo)頭信息正確設(shè)置后,傳遞到Web服務(wù)器,Web服務(wù)器根據(jù)要求作出回應(yīng)。
HttpWebResponse類繼承自WebResponse類,專門處理從Web服務(wù)器返回的HTTP響應(yīng),這個(gè)類實(shí)現(xiàn)了很多方法,具有很多屬性,可以全面處理接收到的互聯(lián)網(wǎng)信息。在HttpWebResponse類中,對(duì)于大多數(shù)通用的HTTP標(biāo)頭字段,都有獨(dú)立的屬性與其對(duì)應(yīng),程序員可以通過這些屬性方便的訪問位于HTTP接收?qǐng)?bào)文標(biāo)頭字段中的信息,本例中用到的HttpWebResponse類屬性為:ContentLength 即接收內(nèi)容的長度。
有了以上的了解后,下面看看這兩個(gè)類的用法,要?jiǎng)?chuàng)建HttpWebRequest對(duì)象,不要直接使用HttpWebRequest的構(gòu)造函數(shù),而要使用WebRequest.Create方法初始化一個(gè)HttpWebRequest實(shí)例,如:
HttpWebRequest hwr=(HttpWebRequest)WebRequest.Create(//www.163.com/);
創(chuàng)建了這個(gè)對(duì)象后,就可以通過HttpWebRequest屬性,設(shè)置很多HTTP標(biāo)頭字段的內(nèi)容,如hwr.AddRange(100,1000); 設(shè)置接收對(duì)象的范圍為100-1000字節(jié)。
HttpWebReques對(duì)象使用GetResponse()方法時(shí),會(huì)返回一個(gè)HttpWebResponse對(duì)象,為提出HTTP返回報(bào)文信息,需要使用HttpWebResponse的GetResponseStream()方法,該方法返回一個(gè)Stream對(duì)象,可以讀取HTTP返回的報(bào)文,如:首先定義一個(gè)Stream對(duì)象 public System.IO.Stream ns; 然后 ns=hwr.GetResponse ().GetResponseStream (); 即可創(chuàng)建Stream對(duì)象。
有了以上的準(zhǔn)備知識(shí)后下面開始設(shè)計(jì)我們的多線程互聯(lián)網(wǎng)文件的下載程序,首先打開Visual Studio.Net集成開發(fā)環(huán)境,選擇文件、新建、項(xiàng)目,然后選擇Visual C#項(xiàng)目,在向?qū)в疫吜斜砜蛑羞x中Windows應(yīng)用程序,輸入項(xiàng)目名稱,如本例為:httpftp, 然后選擇確定按鈕,向?qū)ё詣?dòng)生成了一個(gè)Windows應(yīng)用程序項(xiàng)目。首先打開窗口設(shè)計(jì)器設(shè)計(jì)應(yīng)用程序窗口,增加如下控件:
一個(gè)列表框 listBox1, 三個(gè)文本標(biāo)簽 label1-label3, 三個(gè)文本框 textBox1-textBox3, 一個(gè)開始接收按鈕 button1。
控件定義代碼是:
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;//網(wǎng)絡(luò)功能
using System.IO;//流支持
using System.Threading ;//線程支持
增加如下的程序變量:
public bool[] threadw; //每個(gè)線程結(jié)束標(biāo)志
public string[] filenamew;//每個(gè)線程接收文件的文件名
public int[] filestartw;//每個(gè)線程接收文件的起始位置
public int[] filesizew;//每個(gè)線程接收文件的大小
public string strurl;//接受文件的URL
public bool hb;//文件合并標(biāo)志
public int thread;//進(jìn)程數(shù)
定義一個(gè)HttpFile類,用于管理接收線程,其代碼如下:
public class HttpFile{
public Form1 formm;
public int threadh;//線程代號(hào)
public string filename;//文件名
public string strUrl;//接收文件的URL
public FileStream fs;
public HttpWebRequest request;
public System.IO.Stream ns;
public byte[] nbytes;//接收緩沖區(qū)
public int nreadsize;//接收字節(jié)數(shù)
public HttpFile(Form1 form,int thread)//構(gòu)造方法 {
formm=form;
threadh=thread;
}
~HttpFile()//析構(gòu)方法
{
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 ("進(jìn)程"+threadh.ToString ()+"接收完畢!");
formm.threadw[threadh]=true;
}}
該類和Form1類處于統(tǒng)一命名空間,但不包含在Form1類中。下面定義開始接收按鈕控件的事件響應(yīng)函數(shù):
private void button1_Click(object sender, System.EventArgs e){
DateTime dt=DateTime.Now;//開始接收時(shí)間
textBox1.Text =dt.ToString ();
strurl=textBox2.Text .Trim ().ToString ();
HttpWebRequest request;
long filesize=0;
try
{
request=(HttpWebRequest)HttpWebRequest.Create (strurl);
filesize=request.GetResponse ().ContentLength;//取得目標(biāo)文件的長度
request.Abort ();
}
catch(Exception er)
{
MessageBox.Show (er.Message );
} // 接收線程數(shù)
thread=Convert.ToInt32 (textBox4.Text .Trim().ToString (),10); //根據(jù)線程數(shù)初始化數(shù)組
threadw=new bool[thread];
filenamew=new string [thread];
filestartw=new int [thread];
filesizew=new int[thread]; //計(jì)算每個(gè)線程應(yīng)該接收文件的大小
int filethread=(int)filesize/thread;//平均分配
int filethreade=filethread+(int)filesize%thread;//剩余部分由最后一個(gè)線程完成 //為數(shù)組賦值
for (int i=0;i<thread;i++)
{
threadw[i]=false;//每個(gè)線程狀態(tài)的初始值為假
filenamew[i]=i.ToString ()+".dat";//每個(gè)線程接收文件的臨時(shí)文件名
if (i<thread-1)
{
filestartw[i]=filethread*i;//每個(gè)線程接收文件的起始點(diǎn)
filesizew[i]=filethread-1;//每個(gè)線程接收文件的長度
}
else
{
filestartw[i]=filethread*i;
filesizew[i]=filethreade-1;
} } //定義線程數(shù)組,啟動(dòng)接收線程
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 ();
} //啟動(dòng)合并各線程接收的文件線程
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)//有未結(jié)束線程,等待
{
hb=false;
Thread.Sleep (100);
break;
}
}
if (hb==true)//所有線程均已結(jié)束,停止等待,
{
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;k0)
{
fs.Write (bytes,0,readfile);
}
else
{
break;
}
}
fstemp.Close ();
}
fs.Close ();
DateTime dt=DateTime.Now;
textBox1.Text =dt.ToString ();//結(jié)束時(shí)間
MessageBox.Show ("接收完畢!!!");}
至此,一個(gè)多線程下載文件的程序就大功告成了,注意在輸入本地文件名時(shí),應(yīng)按如下格式輸入:c:\\test\\httpftp\\bin\\d.htm,因\后的字符在C#中是轉(zhuǎn)義字符,線程數(shù)并非越大越好,一般5個(gè)線程就可以了,該程序在Visual Studio.Net 2002開發(fā)環(huán)境及Windows xp 操作系統(tǒng)上通過。
本站文章除注明轉(zhuǎn)載外,均為本站原創(chuàng)或翻譯。歡迎任何形式的轉(zhuǎn)載,但請(qǐng)務(wù)必注明出處、不得修改原文相關(guān)鏈接,如果存在內(nèi)容上的異議請(qǐng)郵件反饋至chenjj@fc6vip.cn
文章轉(zhuǎn)載自:博客園