12篇学通C#网络编程——第三篇 HTTP应用编程(下)

简介:

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

    网络上有很多大资源文件,比如供人下载的zip包,电影(你懂的),那么我们如何快速的进行下载,大家第一反应肯定就是多线程下载,

那么这些东西是如何做的呢?首先我们可以从“QQ的中转站里面拉一个rar下来“。

然后用fiddler监视一下,我们会发现一个有趣的现象:

第一:7.62*1024*1024≈7990914  千真万确是此文件

第二:我明明是一个http链接,tmd的怎么变成n多个了?有意思。

好,我们继续往下看,看看这些链接都做了些什么?

最终,我们发现http协议中有一个Conent—Range字段,能够把我们的文件总大小进行切分,然后并行下载,最后再进行合并,大概我们知道

了什么原理,那么,我们强大的C#类库提供了AddRange来获取Http中资源的指定范围。

 

既然进行了切分,那么首先一定要知道文件的ContentLength是多少,如果对http协议比较熟悉的话,当发送一个头信息过去,服务器返回的

头信息中会包含很多东西,此时我们就知道要下载资源的大概情况,这个就有点“兵马未动,粮草先行“的感觉。


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;
AI 代码解读

 

这里有个决策,到底是以下载量来决定线程数,还是以线程数来决定下载量,由于我们的下载取决于当前的网速,所以在这种场合下更好的方案是

采用后者,这几天在闪存里面两次看到苍老师,肃然起敬,所以决定在不用线程和线程的情况下,看看下载仓老师的速度如何。

图片大小(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();
        }
    }
}
AI 代码解读

 

      在下面的图中可以看出,我们的资源被分成了n段,在217.27KB的情况下,多线程加速还不是很明显,我们可以试试更大的文件,这里我就

在本地放一个133M的rar文件。


        //请求文件
        public static string url = "http://localhost:56933/1.rar";
AI 代码解读

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


目录
打赏
0
0
0
0
83
分享
相关文章
非常实用的Http应用框架,杜绝Java Http 接口对接繁琐编程
UniHttp 是一个声明式的 HTTP 接口对接框架,帮助开发者快速对接第三方 HTTP 接口。通过 @HttpApi 注解定义接口,使用 @GetHttpInterface 和 @PostHttpInterface 等注解配置请求方法和参数。支持自定义代理逻辑、全局请求参数、错误处理和连接池配置,提高代码的内聚性和可读性。
326 3
Python 高级编程与实战:深入理解网络编程与异步IO
在前几篇文章中,我们探讨了 Python 的基础语法、面向对象编程、函数式编程、元编程、性能优化、调试技巧、数据科学、机器学习、Web 开发和 API 设计。本文将深入探讨 Python 在网络编程和异步IO中的应用,并通过实战项目帮助你掌握这些技术。
|
19天前
|
API
已经设置好连接器工厂的HTTP连接器,不会在表单控件里取值应用
这是一个关于通过天眼查API获取企业数据的需求介绍。已实现HTTP连接器调用并成功返回数据,但问题在于如何设计表单:使用单行文本输入企业名称后,触发API查询,将返回的相关数据自动填充到指定的单行文本中,期待高手提供解决方案。
|
5月前
|
公司上网监控:Mercury 在网络监控高级逻辑编程中的应用
在数字化办公环境中,公司对员工上网行为的监控至关重要。Mercury 作为一种强大的编程工具,展示了在公司上网监控领域的独特优势。本文介绍了使用 Mercury 实现网络连接监听、数据解析和日志记录的功能,帮助公司确保信息安全和工作效率。
145 51
学懂C#编程:属性(Property)的概念定义及使用详解
通过深入理解和使用C#的属性,可以编写更清晰、简洁和高效的代码,为开发高质量的应用程序奠定基础。
152 12
CSS Sprites和图标字体在网页图标加载优化中的应用。CSS Sprites通过合并多图标减少HTTP请求,提升加载速度
本文探讨了CSS Sprites和图标字体在网页图标加载优化中的应用。CSS Sprites通过合并多图标减少HTTP请求,提升加载速度;图标字体则以字体形式呈现图标,便于调整样式。文章分析了两者的优缺点及应用场景,并提供了应用技巧和注意事项,旨在帮助开发者提升页面性能,改善用户体验。
65 5
C# 在物联网 (IoT) 应用中的应用
本文介绍了C#在物联网(IoT)应用中的应用,涵盖基础概念、优势、常见问题及其解决方法。重点讨论了网络通信、数据处理和安全问题,并提供了相应的代码示例,旨在帮助开发者更好地利用C#进行IoT开发。
220 3
Unity 游戏引擎 C# 编程:一分钟浅谈
本文介绍了在 Unity 游戏开发中使用 C# 的基础知识和常见问题。从 `MonoBehavior` 类的基础用法,到变量和属性的管理,再到空引用异常、资源管理和性能优化等常见问题的解决方法。文章还探讨了单例模式、事件系统和数据持久化等高级话题,旨在帮助开发者避免常见错误,提升游戏开发效率。
193 4
Go语言中的错误注入与防御编程。错误注入通过模拟网络故障、数据库错误等,测试系统稳定性
本文探讨了Go语言中的错误注入与防御编程。错误注入通过模拟网络故障、数据库错误等,测试系统稳定性;防御编程则强调在编码时考虑各种错误情况,确保程序健壮性。文章详细介绍了这两种技术在Go语言中的实现方法及其重要性,旨在提升软件质量和可靠性。
87 1