唐朝程序员

我来自唐朝

BT源代码学习心得(十四):客户端源代码分析(对等客户连接中的阻塞管理) 转自CSDN:gushenghua的专栏

发信人: wolfenstein (NeverSayNever), 个人文集
标  题: BT源代码学习心得(十四):客户端源代码分析(对等客户连接中的阻塞管理)
发信站: 水木社区 (Wed Aug 17 17:56:50 2005), 文集
(本文包含HTML标记,终端模式下可能无法正确浏览)
    从上一次我们的分析可以看出当对等客户建立连接后,通过握手协议交换信息,这样对
于每个连接都有一个Connection对象,然后有一个SingleDownload和Upload与其对应。这一
次将从握手协议完成后继续分析,然后介绍Choker,阻塞策略控制器的工作原理。
    SingleDownload在初始化时没有做什么特殊的操作,仅仅是创建了一个BadDataGuard对
象和它对应。这个对象是用来统计坏数据的信息,以便确定坏的对等客户的。而Upload对象
在创建的时候,如果自己已经有部分下载数据,就把自己的块拥有情况发送出去
(send_bitfield)。现在就可以来看send_bitfield,我们可以看到在Connection中定义了不
少send_xxx函数用来发送某种消息,并且在Connection对象定义之前,定义了那些消息的类
型的对应的常数项。另外这些send_xxx函数大都调用了_send_message,它的作用就是在要
发送的消息前面添加上它的长度(4个字节),然后发送出去,如果有必要,则放入队列中稍
后发送。这样,每一次_got_message得到的就是消息的内容了。
    现在来看_got_message,它直接取第一个字节就行了,这就是消息的类型。以后我们可
以再检查其它类型的消息,现在我们直接看elif t == BITFIELD这部分,得到对方的块拥有
状况比特数组后,让自己的download对象记录下来,即调用
SingleDownload.got_have_bitfield()。这个函数首先检查自己是否下载完成,然后检查对
方的比特数组中"假"值的个数,如果自己下载完成了且对方的比特数组中"假"值为0,则说
明对方也下载完成了,而两个都在做种的对等客户之间的连接是没有意义的,可以关闭它。
然后self.have = have这一句记录下对方的块拥有情况。所以前面提到StorageWrapper保存
自己的块拥有状况,而对应于每个Connection对象的SingleDownload对象中则保留了对方的
块拥有状况。然后让PiecePicker记录下别人有一块(got_have,complete记录的是自己有一
块,在_SingleTorrent的代码中可以看到)。下面的这个endgame则是一种策略模式,即表示
进入收尾阶段,它检查自己所有的网络请求all_requests(后面还会分析到它),如果对方有
某一块(再次注意,这里self.have[piece]是对方有第piece块,而不是自己),那么发送一
条消息,send_interested()。表示说我对你(所拥有的内容)感兴趣。而如果没有进入收尾
阶段,则只是检查自己有那块没有而对方有,如果有的话,则send_interested()。注意
send_interested()调用一次,对方知道这个意思就行了。
    看Connection._got_message中得到这个消息后怎么处理。是
self.upload.got_interested()。这个函数中维持自己的interested变量为真值,然后通知
choker这件事情,choker.interested则选择是否要进行一次_rechoke()。
    现在应该注意到choked和interested这两个变量,这两个变量的值的意义分别是是否阻
塞和是否感兴趣,它们对下载起到直接开关的作用。在每个SingleDownload和Upload对象中
都有这两个变量。在初始化时,choked都为真而interested都为假,这样就不会有实际的内
容(即种子文件的共享资源)在流通,而要有实际的内容流通必须这两个变量的值和它们初始
化时的值刚好相反才行,也就是说,只有当一方对另外一方感兴趣,而对方又没有拒绝你
(choked=false)的时候,你们之间在这个方向才可能会存在实际的下载流量。另外这两个变
量在网络连接的每个方向都是保持一致的,即Upload中的这两个变量和连接另外一头的
SingleDownload中的这两个变量保持一致,如果有某个变量发生变化,要发送消息给对方,
让对方能继续保持一致。注意这里的保持一致指的是网络连接的两头,而不是本地的
Connection对象对应的SingleDownload和Upload,即本地的Connection的SingleDownload和
对等客户的Upload保持一致,而本地的Connection的Upload和对等客户的SingleDownload保
持一致,而在同一个连接中,下载和上传的两个方向有可能不一致,即一个方向阻塞了,另
一个方向还在下载。
    前面已经注意到,interested这个变量的改变很容易,只要发现对方有自己没有的块,
就会发送这条消息,而choked这个变量的控制就有一定的策略了。Choker就控制所有的连接
(_SingleTorrent级别)的阻塞。它在初始化时即保证_round_robin每十秒种执行一次,而每
次有连接进入时,用connection_made来进行登记,Choker中维护了所有连接的列表,且这
个列表是故意打乱顺序的。在BT的控制策略中,我们还可以多次看到随机打乱顺序的情况发
生,因为有时随机数就是最好的策略。在_round_robin中,首先检查是否已经完成,如果完
成则调用_rechoke_seed(),按照自己已经开始做种的情况进行处理。而计算count%3的余数
就可以保证_round_robin执行三次这部分代码会执行一次,因为count只有在_round_robin
中会被加一。这部分代码就是选择一个choked和interested同时为真的连接放到列表的开头
(不要让喜欢你的人等待太久)。
    在_rechoke()中,首先选择出一些符合解除choked状态的连接(条件是interested和下
载方向的is_snubbed,即当前时间是否距离上次下载到东西的时间过短),然后把所有的这
些连接按照下载的速度排序,由于前面增加了一个负号,因此下载速度最块的排在前面。然
后根据配置项中的最大上传数计算一个配额,这个配额不能等于最大上传数,最多只能对于
这个数减一,从这个列表中取出排名前面的若干位,设置一个mask标志。下面计算出最小上
传数,count。count至少要为一,如果最小上传数比前面的配额还大,那么count也相应增
大。下面就是解除choke状态了,首先mask为1的,无条件解除,如果mask不为1,但是
count还大于0,那么用掉一个count,解除choke状态,其它的连接,一律choke掉。
    Upload的choke和unchoke都是在确定状态改变的情况下,开始向对方通知这一消息。
    这一次结合连接中开始的部分消息交互过程,介绍了choker这一阻塞策略管理器的工作
原理。下一次将开始介绍在连接的双方的已经同意交换数据(choked为假而interested为真
)时的情况。

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

导航