您的位置:首页 > 其它

【对转载进行了部分解释】CRoutingZone的实现机制 以及 process()中一些函数的意义

2012-10-20 22:05 447 查看
Keywords:

CRoutingZone
OnBigTimer
OnSmallTimer
CUinit128
eMule
Kad
二叉树
m_uZoneIndex
m_uLevel

From:http://blog.sina.com.cn/s/blog_4a8804c901008pnr.html

【对原文进一步进行了注释,个人思考,未必正确,还望指教】

CUInt128里面用4个32位整数(m_uData)保存一个128bits的值。它的MSB是m_uData[0]的最高位,LSB是m_uData[3]的最低位。


CRoutingZone代表一个二叉树的节点,他可以是中间节点,也可以是叶子节点。通过其下的CRoutingBin实例是否为空来判断,如果不为空,则为一个叶子节点,反之则是中间节点。CRoutingBin实例中存放的是同这个CRoutingZone具有相同前缀的联系人。

CRoutingZone里面有个member叫m_uLevel,它代表这个CRoutingZone包括了整个ID空间的多少子空间。0表示整个空间,1表示一半。但是这样不利于理解的使用方法。它还可以看成二叉树节点的深度。最重要的是他代表了这个CRoutingZone的前缀范围是m_uLevel。根节点为0,说明根CRoutingZone没有前缀范围,所以任何联系人都可以加入根节点,m_uLevel为3所明这个CRoutingZone的前缀是3BIT,只有ID的前三个BIT和CRoutingZone的前缀相同的联系人才会放到他的下面或他的子树下面。

【Ex:但是真正在eMule的Kad中这个前缀是如何设计的呢?其实无所谓,只是在eMule中的peer更新机制中有参数与这些前缀的长度有关】

CRoutingZone下有m_pSubZones[2]这样一个数组指针,用来存放二叉子树。在当前的子树深度Level上,同本地ID最近的联系人放在m_pSubZones[0]及其子树下面,同本地较远的联系人放在m_pSubZones[1]及其子树下面。也就是比较联系人的ID和本地ID的距离,然后找到对应子树深度Level上的那一个BIT值,为0表示和本地ID更近,为1则远。这样的表示方法虽然和Kademlia描述不太一样,但是并不冲突。如果本地ID在这一级上的BIT为1,则所有在这一级为0的联系人放到m_pSubZones[1]中,为1的联系人放到m_pSubZones[0]中。而Kademlia中描述是直接按照联系人ID在这一位的BIT值(而不是距离)来存放到不同的子树中去的(即先放0,再放1,不管本地ID在当前级的bit是多少)eMule的方法仅仅是让两棵子树调换了一下位置,但结构和父子关系是不变的

【要知道在二叉树中,左右节点是严格区分的,和普通树中的概念有区分。这里左节点放更近的,右节点次之。】

CRoutingZone里有个m_uZoneIndex,不太好理解,注释也看不太懂。仔细研究后发现,它和m_uLevel一起定义了这个CRoutingZone在前缀上与本地ID的距离。比如CRoutingZone的前缀是4,本地ID前4BIT为“1101”,一个联系人的ID前4BIT为“1100”,它的CRoutingZone位于二叉树的位置从根节点开始其路径是0->0->0->1(0表示子树0,1为子树1),m_uZoneIndex
则为“0001”,也就是CRoutingZone位于二叉树的位置,应为联系人是按照距离来对应到二叉树的某个节点上的。(分析这个费心了,我也看了好久才理解)

根据异或运算的特点,知道了距离和本地ID前缀,我们就可以通过再次异或运算求出联系人ID前缀。

【这是当然的,如果都是0,表示和本地ID对应位置一致,是1就表示反之了】

【异或就是不进位的加法】

m_uZoneIndex还有另一个作用,那就是记录了CRoutingZone在某一深度的序号。在同一深度,CRoutingZone结点的序号是递增的,离本地ID越远,序号也就是m_uZoneIndex越大。他可以用来控制每个结点的分裂,来避免结点的指数性增长。

【这句很好理解,因为与本地节点一致,就放在左子节字,并且m_uZoneIndex当前位就为0,以上文的例子来说,最近的当然是:0000,其次就是0001,表示左边子树的右叶子,然后就是0010,就是右边子树的左叶子,0011:右边子树的右叶子。当然这个左右划分是为了理解,在实际实现中,这里是0,1两个数组序号】

每次建立一个新的CRoutingZone,他都会在CKademlia里的m_mapEvents里加入一个记录。CKademlia在Process()函数里会对每个记录的CRoutingZone进行操作。一旦这个CRoutingZone分裂出两个子CRoutingZone时,它就会将自己从m_mapEvents里面拿掉。所以只有叶子结点才会被执行到。具体的操作是在CKademlia::Process()里根据时间来执行的。

CKademlia::Process()是个普通函数,他在CUploadQueue::UploadTimer()中被调用。而UploadTimer是一个定时器的CallBack函数,每隔100ms调用一次。也就是说Process()函数是每100ms运行一次。



CRoutingZone里面有一个m_tNextBigTimer,表示的是下一次运行OnBigTimer的时间(以秒来计算)。

CKademlia也有一个m_tBigTimer的时间,表示下次CKademlia::Process() 检查每个CRoutingZone的m_NextBigTimer的时间。

m_tBigTimer每次更新为当前时间加上10秒钟

CRoutingZone::m_tNextBigTimer的更新每次为当前时间加上1小时

总体来说就是CKademlia每10秒钟检查每个登记了的CRoutingZone的NextBigTimer,如果到了就执行OnBigTimer()。

而CRoutingZone设置NextBigTimer来控制每1个小时运行一次OnBigTimer()。

【到底怎么回事?其实因为CKad..中维护了很多CRoutingZone对象,它要在一个较短时间内,这里为10s来检查这些CRZ对象,那个需要OnBigTimer了,但是对于CRZ对象本身,则是设置了1个小时,直到这个时间到了,才可能触发。当然这就需要Process不断运转了,前文提到100ms就循环,够可以的吧。】

【再看小Timer,就是OnSmallTimer,用于检测联系人在线否,发送hello req消息,1分钟就执行一次。要这么快么?】

【我之前误解了,其实process并不处理报文,只有processpacket处理报文,所以它的tiimer设置主要是为了更新状态等】

OnBigTimer()里面只是查找一个假想的Peer,目的是获得更多的在这个CRoutingZone里面的实际Peer信息。必须是叶子节点才能执行。并且还好满足下列条件:m_uZoneIndex < KK || m_uLevel < KBASE || m_pBin->GetRemaining() >= (K*.8)。【KK=5,KBASE=4, K=10】

【这个方法很巧妙,其实在里面构建的随机查找的ID,前缀是根据m_uZoneIndex 生成的,后续位数通过128-m_uLevel得到,然后通过通过Uint128的构造函数随机补充为一个128位的ID】

【查找过程:先找到自己联系人中的相近的(CSearch的Go函数中),然后向他们发送执行 SendFindValue(),发送KADEMLIA2_REQ消息.】

1)m_uZoneIndex < KK:每一深度只能从最靠近自己的KK个CRoutingZone可以查找更多的Peer。

【这个很好懂,如果你理解了前文m_zZoneIndex的含义】
因为查找Peer很可能会导致CRoutingZone的分裂,如果不控制每一层可分裂的CRoutingZone数目,则回到CRoutingZone以指数方式分裂,导致太多的CRoutingZone。

【“Peer很可能会导致CRoutingZone的分裂”没看懂?其实是这个样子的,尽管这次查找并没有明确目的,但是一旦查找,返回的信息里面就有新peer,如果我们查找的目标ID与本地ID太远,则可能导致有很多较远的ID被返回(查找过程总是返回与查找目标相近的节点),导致太多的CRz对象产生(这与我们的想法是不同的,那我们到底为什么随机查找呢,其实就是为了更新peer,越近越好),理解了吧。其实是一个很复杂的过程的,所以一定要搞清楚KAD网络的基本原理。】

2)m_uLevel < KBASE:由于有前面的限制,最开始深度的CRoutingZone分裂不会很多,导致较远的Peer的信息不是很多。使用这个条件可以增多点对较远Peer的数量的保存。

【要注意了,前面条件一其实很苛刻的,那么二其实是有点区别的,它只规定了级别,距离反而可以扩大点。由于条件是并集,因此其实可以利用这个条件调整peer来源】

3)m_pBin->GetRemaining() >= (K*.8):确保每个叶子CRoutingZone有一些Peer在里面。

我刚开始担心这个会不会导致较远的CRoutingZone也会不断的分裂,但是后来发现在分裂前CRoutingZone会调用CanSplit来检查自己是否可以分裂,使用的判断条件前两个和这里的前两个是一样的,这样就保证了较远的CRoutingZone不会继续分解。

【理解了条件1,2,再看看cansplit的源码,这个不难理解了】

在CKademlia::Start里,会new一个CRoutingZone实例,代表二叉树的根。CRoutingZone的构造含函数发现自己是根的话,就会从文件中读取上次保存的联系人信息,并且使用这些信息来建立二叉树。

【怎么建呢,看看Process以及里面的OnBigTimer和OnSmallTimer就知道了】


CRoutingZone的m_tNextSmallTimer是用来定期检查Peer的状态的(利用hello_req消息)。每隔1分钟,OnSmallTimer()就会被运行一次。联系人有一个Type,这个Type定义了联系人的在线状态。这个状态根据联系人在在线时间而变化:

刚刚加入的联系人,Type=3;

【check the function: UpdateType()】

1) 在线时间小于一个小时的,Type=2;

2) 在线时间大于一个小时小于两个小时的,Type=1;

3) 在线时间大于两个小时的,Type=0;

4) 准备删除的联系人,Type=4。

OnSmallTimer()同样只是在叶子CRoutingZone里可以运行。函数每次运行时会从CRoutingZone的联系人中找到最老的那个联系人,如果那个联系人的有效时期已过,并且Type!=4(防止重复检查)就将向对方发送Request来检查联系人的在线状态。OnSmallTimer()里面同样进行删除不在线(或者称为没有反应)的联系人,那些Type=4并且已经过期了的联系人就会被删除。

一旦联系人回答了请求,他的Type就会被设置为0~2,而且有效时间被重新加长,这样在OnSmallTimer()中就不会删除那些在线的联系人。一个联系人的周期:

1、刚刚加入:Type=3,有效期=0。OnSmallTimer()将立刻检查他的情况,同时Type++。

2、联系人回答:Type=2,有效期=1小时;联系人没有回答,下一次OnSmallTimer()会删除掉它。

3、1小时后,联系人过期,OnSmallTimer()再次发送一个请求,同时Type++,为3,而不是4。

4、联系人回答:Type=1或2,有效器=1.5小时;联系人没有回答,OnSmallTimer()再次发送Request,Type++,为4,联系人再不回答,就删除它。

再次Note that:

【1】process每100ms就执行一次;
【2】CKad类对象每10s就onBigTimer一次,用来检查当前待检查的CRz对象;(会通过查找一定范围内的随机ID来更新Peer)
【3】CRz对象默认1小时开放被检测;(跟上面不冲突)
【4】OnSmallTimer不是更新peer,而是来检测Peer状态,从而调整peer的健康状态(type).并删除联系不上的或者不满足条件的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐