深入學習C#網路程式設計之HTTP應用程式設計(下)
第三篇來的好晚啊,上一篇說了如何向伺服器推送資訊,這一篇我們看看如何"快好準"的從伺服器下拉資訊。
網路上有很多大資原始檔,比如供人下載的zip包,電影(你懂的),那麼我們如何快速的進行下載,大家第一反應肯定就是多執行緒下載,
那麼這些東西是如何做的呢?首先我們可以從“QQ的中轉站裡面拉一個rar下來“。
然後用fiddler監視一下,我們會發現一個有趣的現象:
第一:7.62*1024*1024≈7990914 千真萬確是此檔案
第二:我明明是一個http連結,tmd的怎麼變成n多個了?有意思。
好,我們繼續往下看,看看這些連結都做了些什麼?
最終,我們發現http協議中有一個Conent—Range欄位,能夠把我們的檔案總大小進行切分,然後並行下載,最後再進行合併,大概我們知道了什麼原理,那麼,我們強大的C#類庫提供了AddRange來獲取Http中資源的指定範圍。
既然進行了切分,那麼首先一定要知道檔案的ContentLength是多少,如果對http協議比較熟悉的話,當傳送一個頭資訊過去,伺服器返回的頭資訊中會包含很多東西,此時我們就知道要下載資源的大概情況,這個就有點“兵馬未動,糧草先行“的感覺。
var request = (HttpWebRequest)HttpWebRequest.Create(url); request.Method = "Head"; request.Timeout = 3000; var response = (HttpWebResponse)request.GetResponse(); var code = response.StatusCode; if (code != HttpStatusCode.OK) { Console.WriteLine("下載資源無效!"); return; } var total = response.ContentLength;
這裡有個決策,到底是以下載量來決定執行緒數,還是以執行緒數來決定下載量,由於我們的下載取決於當前的網速,所以在這種場合下更好的方案是
採用後者,這幾天在快閃記憶體裡面兩次看到蒼老師,肅然起敬,所以決定在不用執行緒和執行緒的情況下,看看下載倉老師的速度如何。
View Code using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net; using System.Threading; using System.Threading.Tasks; using System.IO; using System.Collections.Concurrent; using System.Diagnostics; using System.Drawing; namespace ConsoleApplication1 { public class Program { public static CountdownEvent cde = new CountdownEvent(0); //每個執行緒下載的位元組數,方便最後合併 public static ConcurrentDictionary<long,byte[]> dic = new ConcurrentDictionary<long,byte[]>(); //請求檔案 public static string url = "https://www.jb51.net/"; static void Main(string[] args) { for (int i = 0; i < 1; i++) { Console.WriteLine("\n****************************\n第{0}次比較\n****************************",(i + 1)); //不用執行緒 //RunSingle(); //使用多執行緒 RunMultiTask(); } Console.Read(); } static void RunMultiTask() { Stopwatch watch = Stopwatch.StartNew(); //開5個執行緒 int threadCount = 5; long start = 0; long end = 0; var total = GetSourceHead(); if (total == 0) return; var pageSize = (int)Math.Ceiling((Double)total / threadCount); cde.Reset(threadCount); Task[] tasks = new Task[threadCount]; for (int i = 0; i < threadCount; i++) { start = i * pageSize; end = (i + 1) * pageSize - 1; if (end > total) end = total; var obj = start + "|" + end; tasks[i] = Task.Factory.StartNew(j => new DownFile().DownTaskMulti(obj),obj); } Task.WaitAll(tasks); var targetFile = "C://" + url.Substring(url.LastIndexOf('/') + 1); FileStream fs = new FileStream(targetFile,FileMode.Create); var result = dic.Keys.OrderBy(i => i).ToList(); foreach (var item in result) { fs.Write(dic[item],dic[item].Length); } fs.Close(); watch.Stop(); Console.WriteLine("多執行緒:下載耗費時間:{0}",watch.Elapsed); } static void RunSingle() { Stopwatch watch = Stopwatch.StartNew(); if (GetSourceHead() == 0) return; var request = (HttpWebRequest)HttpWebRequest.Create(url); var response = (HttpWebResponse)request.GetResponse(); var stream = response.GetResponseStream(); var outStream = new MemoryStream(); var bytes = new byte[10240]; int count = 0; while ((count = stream.Read(bytes,bytes.Length)) != 0) { outStream.Write(bytes,count); } var targetFile = "C://" + url.Substring(url.LastIndexOf('/') + 1); FileStream fs = new FileStream(targetFile,FileMode.Create); fs.Write(outStream.ToArray(),(int)outStream.Length); outStream.Close(); response.Close(); fs.Close(); watch.Stop(); Console.WriteLine("不用執行緒:下載耗費時間:{0}",watch.Elapsed); } //獲取頭資訊 public static long GetSourceHead() { var request = (HttpWebRequest)HttpWebRequest.Create(url); request.Method = "Head"; request.Timeout = 3000; var response = (HttpWebResponse)request.GetResponse(); var code = response.StatusCode; if (code != HttpStatusCode.OK) { Console.WriteLine("下載的資源無效!"); return 0; } var total = response.ContentLength; Console.WriteLine("當前資源大小為:" + total); response.Close(); return total; } } public class DownFile { // 多執行緒下載 public void DownTaskMulti(object obj) { var single = obj.ToString().Split('|'); long start = Convert.ToInt64(single.FirstOrDefault()); long end = Convert.ToInt64(single.LastOrDefault()); var request = (HttpWebRequest)HttpWebRequest.Create(Program.url); request.AddRange(start,end); var response = (HttpWebResponse)request.GetResponse(); var stream = response.GetResponseStream(); var outStream = new MemoryStream(); var bytes = new byte[10240]; int count = 0; while ((count = stream.Read(bytes,count); } outStream.Close(); response.Close(); Program.dic.TryAdd(start,outStream.ToArray()); Program.cde.Signal(); } } }
在下面的圖中可以看出,我們的資源被分成了n段,在217.27KB的情況下,多執行緒加速還不是很明顯,我們可以試試更大的檔案,這裡我就
在本地放一個133M的rar檔案。
//請求檔案 public static string url = http://localhost:56933/1.rar;
現在看一下效果是非常明顯的。
以上就是深入學習C#網路程式設計之HTTP應用程式設計(下)的詳細內容,更多關於C#網路程式設計之HTTP應用程式設計的資料請關注我們其它相關文章!