深入學(xué)習(xí)C#網(wǎng)絡(luò)編程之HTTP應(yīng)用編程(下)
第三篇來的好晚啊,上一篇說了如何向服務(wù)器推送信息,這一篇我們看看如何"快好準(zhǔn)"的從服務(wù)器下拉信息。
網(wǎng)絡(luò)上有很多大資源文件,比如供人下載的zip包,電影(你懂的),那么我們?nèi)绾慰焖俚倪M(jìn)行下載,大家第一反應(yīng)肯定就是多線程下載,
那么這些東西是如何做的呢?首先我們可以從“QQ的中轉(zhuǎn)站里面拉一個(gè)rar下來“。
然后用fiddler監(jiān)視一下,我們會(huì)發(fā)現(xiàn)一個(gè)有趣的現(xiàn)象:
第一:7.62*1024*1024≈7990914 千真萬確是此文件
第二:我明明是一個(gè)http鏈接,tmd的怎么變成n多個(gè)了?有意思。
好,我們繼續(xù)往下看,看看這些鏈接都做了些什么?
最終,我們發(fā)現(xiàn)http協(xié)議中有一個(gè)Conent—Range字段,能夠把我們的文件總大小進(jìn)行切分,然后并行下載,最后再進(jìn)行合并,大概我們知道了什么原理,那么,我們強(qiáng)大的C#類庫提供了AddRange來獲取Http中資源的指定范圍。
既然進(jìn)行了切分,那么首先一定要知道文件的ContentLength是多少,如果對(duì)http協(xié)議比較熟悉的話,當(dāng)發(fā)送一個(gè)頭信息過去,服務(wù)器返回的頭信息中會(huì)包含很多東西,此時(shí)我們就知道要下載資源的大概情況,這個(gè)就有點(diǎn)“兵馬未動(dòng),糧草先行“的感覺。
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;
這里有個(gè)決策,到底是以下載量來決定線程數(shù),還是以線程數(shù)來決定下載量,由于我們的下載取決于當(dāng)前的網(wǎng)速,所以在這種場(chǎng)合下更好的方案是
采用后者,這幾天在閃存里面兩次看到蒼老師,肅然起敬,所以決定在不用線程和線程的情況下,看看下載倉老師的速度如何。
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); //每個(gè)線程下載的字節(jié)數(shù),方便最后合并 public static ConcurrentDictionary<long, byte[]> dic = new ConcurrentDictionary<long, byte[]>(); //請(qǐng)求文件 public static string url = "http://www.dbjr.com.cn/"; 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個(gè)線程 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], 0, dic[item].Length); } fs.Close(); watch.Stop(); Console.WriteLine("多線程:下載耗費(fèi)時(shí)間:{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, 0, bytes.Length)) != 0) { outStream.Write(bytes, 0, count); } var targetFile = "C://" + url.Substring(url.LastIndexOf('/') + 1); FileStream fs = new FileStream(targetFile, FileMode.Create); fs.Write(outStream.ToArray(), 0, (int)outStream.Length); outStream.Close(); response.Close(); fs.Close(); watch.Stop(); Console.WriteLine("不用線程:下載耗費(fèi)時(shí)間:{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("當(dāng)前資源大小為:" + 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, 0, bytes.Length)) != 0) { outStream.Write(bytes, 0, count); } outStream.Close(); response.Close(); Program.dic.TryAdd(start, outStream.ToArray()); Program.cde.Signal(); } } }
在下面的圖中可以看出,我們的資源被分成了n段,在217.27KB的情況下,多線程加速還不是很明顯,我們可以試試更大的文件,這里我就
在本地放一個(gè)133M的rar文件。
//請(qǐng)求文件 public static string url = http://localhost:56933/1.rar;
現(xiàn)在看一下效果是非常明顯的。
以上就是深入學(xué)習(xí)C#網(wǎng)絡(luò)編程之HTTP應(yīng)用編程(下)的詳細(xì)內(nèi)容,更多關(guān)于C#網(wǎng)絡(luò)編程之HTTP應(yīng)用編程的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- C# TcpClient網(wǎng)絡(luò)編程傳輸文件的示例
- C# 網(wǎng)絡(luò)編程之UDP
- c# 網(wǎng)絡(luò)編程之tcp
- c# 網(wǎng)絡(luò)編程之http
- 深入學(xué)習(xí)C#網(wǎng)絡(luò)編程之HTTP應(yīng)用編程(上)
- 淺談C#網(wǎng)絡(luò)編程詳解篇
- 詳解C# 網(wǎng)絡(luò)編程系列:實(shí)現(xiàn)類似QQ的即時(shí)通信程序
- 總結(jié)C#網(wǎng)絡(luò)編程中對(duì)于Cookie的設(shè)定要點(diǎn)
- C# Socket網(wǎng)絡(luò)編程實(shí)例
- C#網(wǎng)絡(luò)編程基礎(chǔ)之進(jìn)程和線程詳解
- c# socket網(wǎng)絡(luò)編程接收發(fā)送數(shù)據(jù)示例代碼
- C#開發(fā)之Socket網(wǎng)絡(luò)編程TCP/IP層次模型、端口及報(bào)文等探討
- C#網(wǎng)絡(luò)編程中常用特性介紹
相關(guān)文章
C#中哈希表(HashTable)用法實(shí)例詳解(添加/移除/判斷/遍歷/排序等)
這篇文章主要介紹了C#中哈希表(HashTable)用法,簡(jiǎn)單講述了哈希表的原理并結(jié)合實(shí)例形式詳細(xì)分析了C#針對(duì)哈希表進(jìn)行添加、移除、判斷、遍歷、排序等操作的實(shí)現(xiàn)技巧,需要的朋友可以參考下2016-06-06用?FieldMask?提高?C#?gRpc?的服務(wù)性能
這篇文章主要介紹了用?FieldMask?提高?C#?gRpc?的服務(wù)性能,FieldMask?是一個(gè)?protobuf?消息,包含一個(gè)名為?paths?的字段,用于指定用于指定讀取操作返回或更新操作修改的字,下文詳細(xì)內(nèi)容,需要的朋友可以參考一下2022-03-03C#在Unity游戲開發(fā)中進(jìn)行多線程編程的方法
這篇文章主要介紹了C#在Unity游戲開發(fā)中進(jìn)行多線程編程的方法,文中總結(jié)了Unity中使用多線程的幾種方式以及一款多線程插件的介紹,需要的朋友可以參考下2016-04-04C#(asp.net)多線程用法示例(可用于同時(shí)處理多個(gè)任務(wù))
這篇文章主要介紹了C#(asp.net)多線程Thread用法,可用于同時(shí)處理多個(gè)任務(wù),以簡(jiǎn)單數(shù)學(xué)運(yùn)算為例講述了Thread類實(shí)現(xiàn)多線程的相關(guān)技巧,需要的朋友可以參考下2016-06-06