网络编程——第三篇 HTTP应用编程(下)
来源:互联网 发布:网络大专快速本科 编辑:程序博客网 时间:2024/05/22 01:36
网络上有很多大资源文件,比如供人下载的zip包,电影(你懂的),那么我们如何快速的进行下载,大家第一反应肯定就是多线程下载,
那么这些东西是如何做的呢?首先我们可以从“QQ的中转站里面拉一个rar下来“。
然后用fiddler监视一下,我们会发现一个有趣的现象:
第一:7.62*1024*1024≈7990914 千真万确是此文件
第二:我明明是一个http链接,tmd的怎么变成n多个了?有意思。
好,我们继续往下看,看看这些链接都做了些什么?
最终,我们发现http协议中有一个Conent—Range字段,能够把我们的文件总大小进行切分,然后并行下载,最后再进行合并,大概我们知道
了什么原理,那么,我们强大的C#类库提供了AddRange来获取Http中资源的指定范围。
既然进行了切分,那么首先一定要知道文件的ContentLength是多少,如果对http协议比较熟悉的话,当发送一个头信息过去,服务器返回的
头信息中会包含很多东西,此时我们就知道要下载资源的大概情况,这个就有点“兵马未动,粮草先行“的感觉。
1 var request = (HttpWebRequest)HttpWebRequest.Create(url); 2 3 request.Method = "Head"; 4 5 request.Timeout = 3000; 6 7 var response = (HttpWebResponse)request.GetResponse(); 8 9 var code = response.StatusCode;10 11 if (code != HttpStatusCode.OK)12 {13 Console.WriteLine("下载资源无效!");14 return;15 }16 17 var total = response.ContentLength;
这里有个决策,到底是以下载量来决定线程数,还是以线程数来决定下载量,由于我们的下载取决于当前的网速,所以在这种场合下更好的方案是
采用后者,这几天在闪存里面两次看到苍老师,肃然起敬,所以决定在不用线程和线程的情况下,看看下载仓老师的速度如何。
图片大小(217.27KB)
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 =
"http://www.pncity.net/bbs/data/attachment/forum/201107/30/1901108yyd8gnrs2isadrr.jpg"
;
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], 0, 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, 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(
"不用线程:下载耗费时间:{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, 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的情况下,多线程加速还不是很明显,我们可以试试更大的文件,这里我就
在本地放一个133M的rar文件。
//请求文件 public static string url = "http://localhost:56933/1.rar";
现在看一下效果是非常明显的。
- 网络编程——第三篇 HTTP应用编程(下)
- C#网络编程——第三篇 HTTP应用编程(下)
- 12篇学通C#网络编程——第三篇 HTTP应用编程(下)
- 网络编程——第二篇 HTTP应用编程(上)
- C#网络编程——第二篇 HTTP应用编程(上)
- 12篇学通C#网络编程——第二篇 HTTP应用编程(上)
- Socket网络编程和HTTP网络应用编程的比较
- 黑马程序员——网络编程(Socket编程)下
- android网络编程——http get
- android网络编程——http post
- android网络编程——http get
- android网络编程——http post
- Android 网络编程——HTTP概述
- android网络编程——http get
- android网络编程——http post
- Linux 系统应用编程——网络编程(基础篇)
- Linux 系统应用编程——网络编程(高级篇)
- Linux 系统应用编程——网络编程(高级篇)
- 网络编程——第二篇 HTTP应用编程(上)
- mongo的安装
- C/C++中extern关键字详解
- 自己动手写编译器、链接器章节划分
- memcache常见现象(一)雪崩现象
- 网络编程——第三篇 HTTP应用编程(下)
- The Swift Programming Language--语言指南--闭包
- GIS开源库shapeLib的使用方法
- kvm 的guest与host共享文件
- sizeof 几个不被注意的
- Web前端之复选框选中属性
- hdu1422 重温世界杯 (dp)
- poi从excel中读取父子关系型(树形)数据结构到数据库
- log4j:WARN Please initialize the log4j system properly解决办法