bittorrent-4.0.3源码分析(一)客户端下载

1. 前言

    最近由于课题研究需要,需要利用BT做p2p传输加速镜像下载,所以在研究bittorrent-4.0.3的运行流程、优化方式以及源码,这里对bt下载三种模式中的btdownloadheadless过程做出分析。

2. 本文分析内容安排

  • 类层次结构
  • 参数解析和配置
  • 下载流程

3. 参数解析和配置

命令行下载在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函数开始下载,下面第四节将介绍下载过程

4. 下载流程

4.1 开启服务、建立连接、设定速度

具体的下载开始于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)正式开始下载,这块儿内容较多,下小节介绍

4.2 正式下载流程

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类,该类具体的将每个文件进行分片并请求数据。

6. 总结

本文笔者在读docker和distribution交互pull数据的源码时,对其用到的源码不能完全理解,而搜到的一篇文章改版而来的,借鉴参考文献。1

7. 作者介绍

梁明远,国防科大并行与分布式计算国家重点实验室(PDL)应届研究生,14年入学伊始便开始接触docker,准备在余下的读研时间在docker相关开源社区贡献自己的代码,毕业后准备继续从事该方面研究。邮箱:liangmingyuanneo@gmail.com

8. 参考文献

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值