唐朝程序员

我来自唐朝

BT源代码学习心得(四):种子文件的生成 转自CSDN:gushenghua的专栏

BT源代码学习心得(四):种子文件的生成

发信人: wolfenstein (NeverSayNever), 个人文集
标  题: BT源代码学习心得(四):种子文件的生成
发信站: 水木社区 (Wed Aug  3 22:22:09 2005), 文集
(本文包含HTML标记,终端模式下可能无法正确浏览)
    在知道种子文件采取的编码方式后,我们现在可以来看一个种子文件具体是如何生成的
了。在BT中,生成种子文件的可执行模块是btmaketorrent.py(命令行模式)或者
btmaketorrentgui.py(图形界面模式),通过分析,可以知道它们最终都将调用函数
make_meta_files进行种子文件的生成,区别仅仅在于提供给这个函数的参数从何而来。命
令行模式下的程序很简单,即直接从命令行下获取参数,GUI部分的程序以后再和下载客户
端的图形界面程序一起分析,现在我们先直接切入正题。
    BitTorrent/makemetafile.py模块中提供函数make_meta_files。它的参数意义如下:
    URL:Tracker的URL地址,在BT的协议设计中,还是需要有个服务器作为tracker来协调
各个客户端的下载的,tracker部分的程序以后会介绍,现在只需要知道这个URL将要作为一
条信息写入到种子文件中即可。
    file:种子文件的来源文件或目录列表(即准备要在BT上共享的资源),注意,这里的列
表意思是该列表中的每一项都为其生成一个种子文件,而此列表中的每一项可以是一个文件
或者是一个目录。
    flag:一个Event对象,可以用来检查是否用户要求中止程序。程序设计得比较合理,
可以在很细的粒度下检查这个Event是否被触发,如果是则中止执行。
    progressfunc:一个回调函数,程序会在恰当的地方调用它,以表示现在的工作进度,
在命令行模式下,这个回调函数被指向在控制台上显示进度信息的函数,在GUI模式下,这
个回调函数则会影响一个图形界面的进度条。
   filefunc:也是一个回调函数,程序会在恰当的地方调用它,以表示现在在处理哪个文
件。
    piece_len_pow2:分块的大小,BT中把要共享的资源分成固定大小的块,以便处理。这
个参数就是用2的指数表示的块的大小,例如当该参数为19的情况下,则表示共享的资源将
被分成512k大小的块为单位进行处理。
    target:目标文件地址,即种子文件的地址。这个参数可以不指定(None),则种子文件
将与公享资源处于同一目录。
    comment:说明。一段可以附加在种子文件内的信息。
    filesystem_encoding:文件系统编码信息。
    make_meta_files的主要工作是进行一系列的检查。例如在开始的时候就检查files的长
度(元素的个数)和target,当files的长度大于1且target不是None的时候就会报错,因为如
果要生成多个种子文件的话,是不能指定target的(这样target只确定了一个种子文件的保
存位置)。接下来检查文件系统的编码问题。然后把files中所有以.torrent结尾的项目全部
刨掉,剩下的作为参数传递给make_meta_file进行处理,注意,这个函数一次生成一个种子
文件。
    下面来看make_meta_file,它一开始计算出块的大小,以2的指数为基础。接下来找到
种子文件的保存地址,如果有target,以target为准,否则如果要对一个目录生成种子文件
,则生成以那个目录名为名称,后缀".torrent"的文件。否则生成以源文件为名称,后缀
".torrent"的文件。
    下面调用函数makeinfo来生成一个"info"。这个info是什么东西呢?继续看。
makeinfo首先检查传给它的path,看看是单个文件还是一个目录。如果是一个目录的话,则
调用subfiles把这个目录下的所有文件全部列出来,这个subfiles设计得比较巧妙,使用堆
栈的方法避免了递归调用。从subfiles得到结果后,首先对它们进行排序。然后使用变量
fs保存这些文件的列表信息,fs是一个list结构,每个元素包含了文件名称和它的大小组成
的二元组。接下来就是记录文件的内容了,下面的这个算法看上去有点晕,其实它的意义是
很明确的,每次从要共享的资源里读取长度为piece_length(就是前面那个以2的指数为基础
计算出来的块的大小)的数据,然后计算它的sha消息摘要值。如何做到这一点呢?就是根据
那个排好序的文件列表,读出piece_length的长度的内容,如果这个文件长度不够,则再读
下一个文件,知道长度够了或者读完所有文件为止。生成一个消息摘要后把它加入到
pieces数组中,再读下一块,直到全部处理完。为一个文件生成info的方法类似,只是更简
单,直接从这个文件中一块一块得处理即可。最后这个makeinfo返回的info是一个字典,它
的数据如下:
    pieces:每一块的消息摘要值的连接。
    piece length:每一块的长度。
    files:文件的列表信息,这里由于文件顺序和生成消息摘要的顺序是相同的,以后BT
的客户端根据种子文件的描述,就可以很清晰得确定原始的文件名和它们的大小,再配以消
息摘要值,就可以检查下载内容是否正确了。
    name:种子文件的内部名称,种子文件可以被随便改名,但是为了识别它方便,内部还
是起了这么一个名称的,通常用要共享的资源来命名它。
    我们注意到flag.isSet多处被检查,其中粒度最小的地方是在读取了一块之后。它返回
后将一路返回到make_meta_files结束,这样用户随时可以中断程序的执行。
    在makeinfo返回info这个字典类型的数据后,再调用check_info这个函数对其内容进行
检查,这个函数定义在BitTorrent/btformats.py模块中,后面在客户端进行下载的时候还
需要检查它。
   最后我们看到的是一个类型为字典的data,其中的元素包含了announce,一个字符串,
creation date,一个整型数据,info,又是一个字典,如果有comment的话,那么还包含了
字符串类型的comment。
    最后把这个类型为字典的data保存到磁盘上,工作就算完成了。怎么对这种比较复杂的
数据类型进行编码以方便保存呢?就是上次提到的bencode。
    所以我们可以看到,一个种子文件就是一个类型为字典的data编码后的情形。

posted on 2007-01-10 17:08  唐朝程序员  阅读(1005)  评论(0编辑  收藏  举报

导航