HTTP多线程下载

Posted moonteam基地

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HTTP多线程下载相关的知识,希望对你有一定的参考价值。

点击蓝字




01


什么是HTTP

HTTP协议(HyperText Transfer Protocol)即为超文本传输协议,是我们上网最经常使用的协议。本文所有例子以HTTP/1.1进行说明,尽管HTTP/2一些网站已经开始使用,QUIC也被Google提出,但是HTTP/1.1至少现在来看仍然是最为普遍的。

HTTP/1.1的一个请求报文大概是这个样子的。如果不太了解HTTP协议实现,推荐阅读David Gourley,Brian Totty的《HTTP权威指南》。


[method] [uri] HTTP/1.1\r\n

[header_name0]:[header_value0]\r\n

[header_name1]:[header_value1]\r\n

[header_name2]:[header_value2]\r\n

...

\r\n

[Content]

02


各程序语言的实现

尽管直接使用socket发送形如上面那样的报文是可以实现的,一般在编程实现的时候一般不会采取如此暴力的方法,一是实现较为复杂,二是不容易很好的处理各种异常情况。以下是各种程序语言推荐的实现方法。

C++------libcurl

Java-----HttpURLConnection

C#-------HttpWebRequest

Python--requests

什么,你说你没有接触过不会使用?没有人天生什么都会的,不会的问题应该看文档解决。

03


多线程在HTTP下载的作用

多线程允许你的程序并发调度各个线程,当一个线程处于阻塞状态的时候,可以去调度其他线程,从而有效减少程序在线程阻塞上等待所浪费的时间。

HTTP也是基于TCP的,socket在执行send()和recv()的时候会将线程阻塞,直到完成了发送/接收数据的任务,这和文件I/O的二进制读写[write()和read()]是类似的。

合理使用多线程并发调度下载任务可以极大的提升下载速度,但是并不是线程越多速度越快,当达到你的网络带宽极限或者各种极限(比如该网站对于单IP的速度限制)的时候,一味的增加线程数只会增加内存的消耗、并发调度上消耗的时间,最后同步下载线程的时候也会花去很多时间并造成程序主线程阻塞(这也是为什么X雷下载到99%的时候会有较长时间的卡顿)。

04


一个比较简单的例子

WARNING:为了便于读者看懂,本例子没有使用C++&libcurl,而是使用python编写,便于理解概念。本例子默认读者会使用python的基本语法、requests库、threading库、线程同步方法,了解HEAD方法、Content-Length头、Range头。


import threading

import requests

down_mutex=threading.Lock()


# 下载线程函数

def down_thread(url,headers,f,start,end):

    pos=start

    if(start>=end):

        return

    headers['Range']='bytes='+str(start)+"-"+str(end)

    req=requests.get(url,headers=headers,stream=True)

    for chunk in req.iter_content(chunk_size=1024):

        if chunk:

            # write file

            down_mutex.acquire()

            f.seek(pos)

            f.write(chunk)

            down_mutex.release()

            pos+=len(chunk)


# 获取url中资源的长度

def down_len(url,headers):

    rhead=requests.head(url,headers=headers).headers

    len=int(rhead['Content-Length'])

    return len


# 多线程下载函数

# 以n_thread个线程下载url的第1-len字节,带上headers头部信息

def fdown(url,headers,f,n_thread,len):

    per_th=int(len/n_thread)

    if(len%n_thread>0):

        n_thread+=1

    # f = open("file_name.ext", "wb")

    threads=[]

    for i in range(n_thread):

        start=per_th*i

        end=per_th*(i+1)

        if(end>len):

            end=len

        end-=1

        th=threading.Thread(target=down_thread,args=(url,headers.copy(),f,start,end))

        th.start()

        threads.append(th)

    for th in threads:

        th.join()

05


效果

本人又双叒叕用bilibili试刀了,直接requests.get单线程下载它的视频大概5Mbit/s,使用上面给出的fdown()之后仅用10个线程就达到了50Mbit/s。

使用方法如下:首先调用down_len函数,获取这个URL的资源的字节数,然后调用fdown多线程下载所有的字节。代码大致是这样的:


url="https://www.bilibili.com/"

headers={

"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"

}

len=down_len(url,headers)

f = open("file_name.ext", "wb")

fdown(url,headers,f,n_thread,len)

f.close()













MNTMDEV@github.com


以上是关于HTTP多线程下载的主要内容,如果未能解决你的问题,请参考以下文章

Go 实战 :如何实现 HTTP 断点续传多线程下载?

windows环境下c语言支持ftp和http多线程下载的客户端

Qt多线程http下载器之一:仿百度网盘的http下载器

HTTP多线程下载

多线程下载图片

python多线程下载文件