最近由于课题研究需要,需要利用BT做p2p传输加速镜像下载,所以在研究bittorrent-4.0.3的运行流程、优化方式以及源码,这里对bt下载三种模式中的btdownloadheadless过程做出分析。
命令行下载在btdownloadheadless.py文件中,首先从__main__开始运行,其中通过下面的语句分别将用户输入的参数和一些配置的初始化赋值给args和config
config, args = configfile.parse_configuration_and_args(defaults,uiname, sys.argv[1:],0, 1)
之后判断用户输入的是一个文件还是一个url,针对不同的参数调用不同的方法读取torrent文件中信息并赋值给metainfo
if config['responsefile']:
h = file(config['responsefile'], 'rb')
metainfo = h.read()
h.close()
elif config['url']:
h = urlopen(config['url'])
metainfo = h.read()
h.close()
else:
raise BTFailure('you need to specify a .torrent file')
最后一步调用DL类的初始化和run函数开始下载,下面第四节将介绍下载过程
具体的下载开始于btdownloadheadless.py中的类DL中,主要是run函数,代码如下:
def run(self):
self.d = HeadlessDisplayer(self.doneflag)
try:
self.multitorrent = Multitorrent(self.config, self.doneflag,
self.global_error)
# raises BTFailure if bad
metainfo = ConvertedMetainfo(bdecode(self.metainfo))
torrent_name = metainfo.name_fs
if config['save_as']:
if config['save_in']:
raise BTFailure('You cannot specify both --save_as and '
'--save_in')
saveas = config['save_as']
elif config['save_in']:
saveas = os.path.join(config['save_in'], torrent_name)
else:
saveas = torrent_name
self.d.set_torrent_values(metainfo.name, os.path.abspath(saveas),
metainfo.total_bytes, len(metainfo.hashes))
self.torrent = self.multitorrent.start_torrent(metainfo,
self.config, self, saveas)
except BTFailure, e:
print str(e)
return
self.get_status()
self.multitorrent.rawserver.listen_forever()
self.d.display({'activity':'shutting down', 'fractionDone':0})
self.torrent.shutdown()
首先是HeadlessDisplayer类,实现的功能主要是显示该客户端下载的状态,比如时间、上传速度(upRate)、下载速率(downRate)、下载比例(percent done)、seed status、peer status等。
接下来调用的是Multitorrent类,定义在download.py中。类中初始化函数如下所示:
def __init__(self, config, doneflag, errorfunc, listen_fail_ok=False):
self.config = dict(config)
self.errorfunc = errorfunc
self.rawserver = RawServer(doneflag, config, errorfunc=errorfunc,
tos=config['peer_socket_tos'])
self.singleport_listener = SingleportListener(self.rawserver)
self._find_port(listen_fail_ok)
self.filepool = FilePool(config['max_files_open'])
self.ratelimiter = RateLimiter(self.rawserver.add_task)
self.ratelimiter.set_parameters(config['max_upload_rate'],
config['upload_unit_size'])
set_filesystem_encoding(config['filesystem_encoding'],
errorfunc)
其中RawServer类实现网络服务器,下载文件的同时提供上传服务。比如该类中实现了函数create_serversockert、start_listening、start_connection等。
SingleportListener类实现位于Encoder.py中,负责管理监听端口和torrent文件。
FilePool类实现位于Storage.py中,设置可以打开的最大文件数,添加要下载的文件等。
RateLimiter类实现位于RateLimiter.py中,设置上传速度和上传块的大小,这里设定的是默认的大小;后面的self.ratelimiter.set_parameters(config['max_upload_rate'],config['upload_unit_size'])
从文件中读取。Multitorrent类介绍完了,继续上面run部分代码分析:
首先metainfo = ConvertedMetainfo(bdecode(self.metainfo))
将torrent文件内容解析,之后判断用户输入的参数是save_as还是save_in从而确定是下载为一个文件还是放入到一个文件夹中,之后self.d.set_torrent_values(metainfo.name, os.path.abspath(saveas),metainfo.total_bytes,len(metainfo.hashes))
设置DownloadDisplay要显示的文件名、下载路径、大小、片数。
之后self.torrent = self.multitorrent.start_torrent(metainfo,self.config, self, saveas)
正式开始下载,这块儿内容较多,下小节介绍
start_torrent函数的定义位于download.py中,如下所示:
def start_torrent(self, metainfo, config, feedback, filename):
torrent = _SingleTorrent(self.rawserver, self.singleport_listener,
self.ratelimiter, self.filepool, config)
self.rawserver.add_context(torrent)
def start():
torrent.start_download(metainfo, feedback, filename)
self.rawserver.add_task(start, 0, torrent)
return torrent
_SingleTorrent根据上一小节中已经初始化的RawServer、SingleportListener、FilePool、RateLimiter的multitorrent等构件SingleTorrent类;接下来将构建的torrent加入到rawserver中;再向后定义了个函数start(),在该函数中调用了torrent.start_download(),具体实现为:
def start_download(self, *args, **kwargs):
it = self._start_download(*args, **kwargs)
def cont():
try:
it.next()
except StopIteration:
self._contfunc = None
def contfunc():
self._rawserver.external_add_task(cont, 0, self)
self._contfunc = contfunc
contfunc()
其中,_start_download函数中,设定了最大上传连接的个数,该个数跟上传速率相关;之后设定了要下载每个文件的路径,并调用Storage类,将一次要下载的所有文件连接成一个文件,为每个文件分配空间;之后调用Storage.check_fastresume函数判断fastresume是否和文件匹配;之后又调用了StorageWrapper类,该类具体的将每个文件进行分片并请求数据。
本文笔者在读docker和distribution交互pull数据的源码时,对其用到的源码不能完全理解,而搜到的一篇文章改版而来的,借鉴参考文献。1
梁明远,国防科大并行与分布式计算国家重点实验室(PDL)应届研究生,14年入学伊始便开始接触docker,准备在余下的读研时间在docker相关开源社区贡献自己的代码,毕业后准备继续从事该方面研究。邮箱:[email protected]