网络编程——第三篇 HTTP应用编程(下)

http://www.cnblogs.com/ShaYeBlog/archive/2012/09/11/2680536.html

 

第三篇来的好晚啊,上一篇说了如何向服务器推送信息,这一篇我们看看如何"快好准"的从服务器下拉信息。

    网络上有很多大资源文件,比如供人下载的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)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
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[]>();
  
        //请求文件
  
        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";

现在看一下效果是非常明显的。

 
 
分类: 网络编程
posted @ 2015-04-27 17:21  dps002  阅读(130)  评论(0编辑  收藏  举报