zoukankan      html  css  js  c++  java
  • 网络编程——第三篇 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(" **************************** 第{0}次比较 ****************************", (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";

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

     
     
  • 相关阅读:
    Java中的国际化
    springcloud介绍
    SpringMVC之请求和响应
    JAVA坦克大战系列10-高效雷达(下)
    CF920F SUM and REPLACE
    luoguP4141 消失之物
    luoguP2843 暗杀
    luoguP5521 [yLOI2019] 梅深不见冬
    CF940E Cashback
    CF1051D Bicolorings
  • 原文地址:https://www.cnblogs.com/dps001/p/4460751.html
Copyright © 2011-2022 走看看