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多线程下载的主要内容,如果未能解决你的问题,请参考以下文章