深入學(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 千真萬(wàn)確是此文件
第二:我明明是一個(gè)http鏈接,tmd的怎么變成n多個(gè)了?有意思。

好,我們繼續(xù)往下看,看看這些鏈接都做了些什么?


最終,我們發(fā)現(xiàn)http協(xié)議中有一個(gè)Conent—Range字段,能夠把我們的文件總大小進(jìn)行切分,然后并行下載,最后再進(jìn)行合并,大概我們知道了什么原理,那么,我們強(qiáng)大的C#類庫(kù)提供了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)合下更好的方案是
采用后者,這幾天在閃存里面兩次看到蒼老師,肅然起敬,所以決定在不用線程和線程的情況下,看看下載倉(cā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.fzitv.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個(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#表達(dá)式樹實(shí)現(xiàn)對(duì)象的深克隆(實(shí)例詳解)
C# 的表達(dá)式樹提供了一個(gè)強(qiáng)大的機(jī)制,可以將代碼以數(shù)據(jù)結(jié)構(gòu)的形式表示出來,使得代碼可以在運(yùn)行時(shí)進(jìn)行檢查、修改或執(zhí)行,這為動(dòng)態(tài)查詢生成、代碼優(yōu)化和動(dòng)態(tài)編程提供了很多可能性,這篇文章主要介紹了使用C#強(qiáng)大的表達(dá)式樹實(shí)現(xiàn)對(duì)象的深克隆,需要的朋友可以參考下2024-05-05
C#實(shí)現(xiàn)網(wǎng)絡(luò)通信共享庫(kù)NetShare的使用示例
本文主要介紹了C#實(shí)現(xiàn)網(wǎng)絡(luò)通信共享庫(kù)NetShare,網(wǎng)絡(luò)通信共享庫(kù)NetShare用于保證客戶端與服務(wù)器通信數(shù)據(jù)包的規(guī)范和統(tǒng)一,感興趣的可以了解一下2023-11-11
C#操作SQLite數(shù)據(jù)庫(kù)方法小結(jié)
這篇文章介紹了C#操作SQLite數(shù)據(jù)庫(kù)的方法,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06
c# 動(dòng)態(tài)加載dll文件,并實(shí)現(xiàn)調(diào)用其中的簡(jiǎn)單方法
下面小編就為大家?guī)硪黄猚# 動(dòng)態(tài)加載dll文件,并實(shí)現(xiàn)調(diào)用其中的簡(jiǎn)單方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-01-01
C#中Kestrel和IIS服務(wù)器下的同步與異步配置
本篇文章主要講解什么是Kestrel和IIS服務(wù)器和特點(diǎn),以及他們?nèi)绾闻渲猛脚c異步,具有一定的參加價(jià)值,感興趣的可以了解一下2023-08-08

