唐朝程序员

我来自唐朝

BT源代码学习心得(六):跟踪服务器(Tracker)的代码分析(初始化) 转自CSDN:gushenghua的专栏

BT源代码学习心得(六):跟踪服务器(Tracker)的代码分析(初始化)

发信人: wolfenstein (NeverSayNever), 个人文集
标  题: BT源代码学习心得(六):跟踪服务器(Tracker)的代码分析(初始化)
发信站: 水木社区 (Mon Aug  8 11:30:43 2005), 文集
(本文包含HTML标记,终端模式下可能无法正确浏览)
    Tracker在BT中是一个很重要的部分。这个名词我注意到以前的文章中都是直接引用,
没有翻译过来,想了一下,决定把它翻译成跟踪服务器。
    在BT下载中,种子文件表明了要下载的文件的信息和对它进行检查的消息摘要码,但是
每个对等客户(peer,以后我把peer全部翻译成对等客户,以区别client)要获取其它对等客
户的信息时,还是要和跟踪服务器联系的。跟踪服务器上面不保存任何和种子所代表的内容
有关的文件,它只记录所有下载该种子的机器的IP地址,端口等信息,并在客户向它请求是
返回一些这样的信息列表,具体的实际内容,由对等客户之间完成交互。
    跟踪服务器的代码实现在BitTorrent/track.py中,在bttrack.py中只是很简单得一行

    track(argv[1:])
    这样就把参数传到track.py的track函数。track函数本身也比较简单,处理参数和相关
的配置文件,建立一个RawServer,然后用create_serversocket创建服务器套接字,然后开
始服务。关于在BT中使用网络服务上次已经有很详细地介绍,这里不再重复。只是针对
tracker函数的具体情况,分析一下运行到listen_forever后的情况,首先,建立了
Tracker对象,打开了在某个端口(config['port'])侦听的网络服务,这个函数的处理对象
是一个HTTPHandler。所以我们要分析程序的流程只需要先分析Tracker的初始化函数,看看
它创建后都做了些什么,然后再看HTTPHandler实际分析它的网络协议。
    在Tracker对象的初始化函数中,首先还是对各种变量的初始化。然后要从一个状态文
件中进行一些状态恢复,也就是恢复state变量。这个变量中的值很重要,我们可以需要从
一些地方来得知它的结构,状态文件的读取和保存出得不到它的信息,因为这两处的实现方
式就是bencode和bdecode,只能保证无论state的结构是什么都能合适得被保存和恢复,由
此又看出bencode编码设计的巧妙。但是有一个函数对我们分析state的内部结构很有帮助,
那就是statefiletemplate,这个函数检查state中的值是否合法,因此我们可以从这里得到
state的一些结构信息。
    首先,state必须是一个字典类型的变量。然后检查每一项的值。如果发现一项关键字
是'peers',那么它的值必须也是一个字典,这个字典是一个以种子文件的信息部分的消息
摘要值为关键字的字典,由于sha摘要算法比较好得满足了摘要算法的要求,即不同的种子
文件它们生成相同摘要的概率极小。而且由于这是由种子文件的内容生成的摘要值,因此即
使把种子文件改名,还是可以识别出来是哪个种子文件。因此'peers'的值可以看成是为每
一个种子文件记录的信息,那么为每个种子文件记录的是什么信息呢?这个信息又是一个字
典,这次以每个对等客户的ID为关键字,每个对等客户在连接到跟踪服务器的时候都会为自
己生成一个ID,这个ID怎么生成的以后看客户端的代码可以知道,现在我们知道的是,它的
长度必须为20。这个字典的值,嗯,又是个字典,不过这个字典的意义就明显多啦,包括了
IP是多少,端口是多少,还剩多少没有下载完。因此state的内容可以看成是这样的:{'
peers':{},...},其中peers的结构是这样的:{hash1:{ID1:{'ip':xxx.xxx.xxx.xxx,'
port':xxxx,left:XXXX},ID2:{'ip':yyy.yyy.yyy.yyy,'port':yyyy,left:YYYY},...},
hash2:{...},...}。以上是state中'peers'这一项。'completed'这一项就相对结构简单了
,它记录的是每个种子文件的下载完成情况,它的结构是个字典,以每个种子的信息部分的
消息摘要值为关键字,而对应的值就是一个整数,表示该种子文件已经有多少人完成了下载
。接下来是'allowed'项,这项记录了该跟踪服务器所关注的所有的种子的信息,仍然以信
息部分的消息摘要值为关键字,内容就是该种子文件的实际信息,从后面的分析(对
BitTorrent/parsedir.py的分析)可以知道是哪些信息,另外由于之前对种子文件的内部结
构我们已经比较清楚,所以也可以猜出部分。state中还有'allowed_dir_files'项,这一项
也是记录文件信息的字典,但它是以每个文件的文件名为关键字(而不是消息摘要值),每个
文件的项目是一个列表,结构如下:[(文件修改时间,文件大小),消息摘要值],就是说,这
个以文件名为关键字的字典它的每一个值都是一个列表,这个列表有两个元素,第一个元素
是一个二元组,内容是文件修改时间和文件大小,第二个元素是消息摘要值。最后,我们注
意到statefiletemplate在处理'allowed'项和'allowed_dir_files'项时还有一些额外的检
查代码,即所有在'allowed'项里面出现的元素,它的消息摘要值都必须在'
allowed_dir_files'项中出现,且'allowed_dir_files'中所有的项中的值的消息摘要部分
必须在'allowed'中出现,另外'allowed_dir_files'中不得出现重复的消息摘要值('
allowed'项本身就以消息摘要值为关键字,而字典的关键字已经保证不会重复)。
    因此现在我们知道了state中的注意部分的结构。下面我们注意这两句:
    self.downloads    = self.state.setdefault('peers', {})
    self.completed    = self.state.setdefault('completed', {})
    这样就把state中的'peers'和'completed'的值传到了downloads和completed中,更重
要的是,以后在跟踪服务器的运行过程中,如果'peers'和'completed'的值发生改变(那简
直是一定的),state中的相应值也会发生变化,这样,保存dfile时,就可以及时更新
state的值了。以后我们分析跟踪服务器运行过程的时候少不了和它们打交道,现在我们可
以先记住,downloads保存了所有的下载的客户端的信息,completed保存所有的种子的下载
完成情况的统计信息。
    下面的这个for循环根据配置文件处理NAT的问题,以及计算种子的个数。completed只
是记录所有下载完成的客户的数目,而只有已经下载完成(left=0),但是还在downloads中
出现(即下载完毕但是没有关闭客户端)的客户端才算是一个种子。这里我们可以很容易得看
出,seedcount是一个以信息摘要为关键字,整型为值的统计种子数的一个字典。
    下面是一个计算的变量,times表示了每个种子(以信息摘要为关键字)中每个客户(以客
户ID为关键字)的上次的有活动的时间。接下来增加了两个任务,每隔一段时间保存一下
dfile,并且检查下载的客户端是否已经有很长时间没有反应的。
    接下来准备一个日志文件,并试图把标准输出重定向到这个日志文件中。
    最后要去寻找该跟踪服务器所关注的所有的种子,即parsedir,这个函数可以自己去看
,相信在知道了种子文件的编码格式和前面的状态中的项的要求后,不难分析。总得说来,
这个函数做了以下事情,即寻找某个目录下所有的.torrent文件,把这些文件中的信息读取
进来,并且排除错误,重复等等不合要求的,然后进行加工,输出符合要求的结果,储存在
allowed和allowed_dir_files中,进而影响state。
    现在tracker对象已经建立起来,它已经有它要进行跟踪的所有种子的信息,并且准备
好了维护所有连接进来的客户的列表,因此它可以正式开始提供跟踪服务了。下一次我们就
可以看看tracker动起来的效果。
 

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

导航