天天看点

eMule源代码分析(四)

emule中的信誉机制

信誉机制在P2P系统中有非常重要的作用。为了使用户更加愿意共享自己的资源,需要有一些机制能够让对整个P2P系统贡献更大的用户有更多的激励。在 emule中,激励机制的设计方案是tit-for-tat这种最直观的方案。这种方案的意义就是最简单的如果别人对你好,那么你也对别人好。

下面看实际的实现。CClientCreditsList和CClientCredits类负责emule中的信誉机制。我们再次见到这种列表和元素之间的关系,不必再重复那些语言。和信誉相关的信息是需要永久保存的,这样才有意义,因此CClientCreditsList提供了LoadList和 SaveList方法。我们另外注意到,CClientCredits类可以使用CreditStruct结构来创建,而CreditStruct结构只包含静态信息。主要是上传量和下载量等。

信誉机制的信息需要有一定的可靠性,在emule中采用了数字签名的方式来做到这一点。 Crypto++库为emule全程提供和数字签名验证相关的功能。CClientCreditsList在创建时,会装载自己的公钥私钥,如果没有的话,会创建一对。CClientCreditsList中包含的有效的信息都是经过其它人数字签名的,所以更加有信服力。在实际使用中,这些信息和自己的私钥要注意保存。重装emule后应该把配置文件目录先备份,这样能够保留自己辛辛苦苦积攒的信誉。 

下载任务即部分文件的表示

CPartFile 类是emule中用来表示一个下载任务的类。从它的名字也可以看出来,这就是一个还没有完成的文件。当一个下载任务被创建时,emule会在下载目录中创建两个文件,以三位数字加后缀part的文件,例如001.part,002.part等。还有一个以同样的数字加上.part.met的文件,表示的是对应文件的元信息。part文件会创建得和原始文件大小一样,当下载完成后,文件名会修改成它本来的名称。而事实上,诸如这个文件原来叫什么名称,修改日期等等信息都在对应的.part.met元文件中。.part.met中还包含了该文件中那些部分已经下载完成的信息。

CPartFile 类中Gap_Struct来表示文件的下载情况,一个Gap_Struct就是一个坑,它表示该文件从多少字节的偏移到多少字节偏移是一个坑。下载的过程就是一个不断填坑的过程。CPartFile类中有个成员变量gaplist就是该文件目前的坑的状况列表。需要主要的是有时填了坑的中间部分后,会把一个坑变成两个坑。坑的列表也会被存进.part.met中。

CPartFile类的代码很庞大,但是这是必须的。首先,它的创建就有几种可能,从搜索文件CSearchFile中创建,这种情况发生在用户搜索到他想要的文件后点击下载时发生。从一个包含了ed2k链接的字符串中创建,它会提取出该ed2k链接中的信息,并用来创建CPartFile。剩下的一种,就是当emule程序重启后,恢复以前的下载任务。这时就是去下载目录中寻找那些.part和.met文件了。另外它还需要不断得处理下载到的数据,为了减少磁盘开销,使用了Requested_Block_Struct结构来暂存写入的数据。它内部维护一个CUpDownClient的列表,如果知道了该文件的一个新的来源信息,就会创建一个对应的CUpDownClient。后者是emule中代码量最大的类。它还要把它的状态用彩色的条装物显示出来提供给GUI。

前面提到的AICH机制对于最大程度得保证下载文件的正确性以及尽量减少重复传输都有很大的帮助,在下载的过程中,该机制会经常对下载到的数据进行校验。

最后提一下它的Process方法。该方法是emule中为了尽量减少线程的使用而采取的一种有一些类似于轮询的机制。其它很多类中也有Process方法,这个方法要做的事情就是在一些和日常运行有关的事情,例如检查为了下载该文件而链接到自己的各个客户端的状态,向它们发送下载请求等。

下载任务队列

CDownloadQueue 是下载队列类。这个队列中的项目是CPartFile指针。因此和emule中出现的很多其它的列表类一样,它需要能够提供对这个列表中的元素进行增加,查询,删除的功能。例如查询的时候能够根据该文件的hashID或者索引来进行查询。CDownloadQueue同时还要完成一些统计工作。

和其它的列表类不一样的是,它的所有元素的信息并不是集中存放于一个文件,而是对应于每一个下载任务,单独得存放在一个元信息文件(.part.met) 中,因此当该类进行初始化的时候,它需要寻找所有可能的下载路径,从那些路径中找到所有的.part.met文件,并且试图用这些文件来生成 CPartFile类,并且将这些通过.part.met文件正确生成的CPartFile类添加到自己的列表中,同样,在退出时,所有的下载任务的元信息也是自行保存,不会合成为一个文件。

CDownloadQueue中的Process方法的主要任务就是把它的列表中的 CPartFile类中的Process方法都调一遍,另外主要的一些关于下载情况的统计信息也是在每一轮的Process后进行更新的。从这里我们也可以看出Process方法在emule中的意义,就是一个需要经常执行的方法,通过经常执行它们来完成日常工作,而且所有的这些Process方法肯定是顺序执行,因此可以减少很多多线程的同步之类的问题。emule中已经尽量减少了多线程的使用,但是在很多地方如果多线程是不可避免的话,也不会排斥。 

上传任务队列

CUploadQueue 是上传队列类。这个列表类中只有以CUpDownClient为元素的列表,它和其它列表类还有一个很大的不同就是它所保存的信息都不需要持久化,即不需要在当前的emule退出后还记住自己正在给谁上传文件,然后下次上线的时候再继续给他们传,这在大部分情况下是没有意义的。

上传队列类列表中有两个列表,上传列表和排队列表。当一个收到一个新的下载请求后,它会把对应的客户端先添加到排队列表中,以后再根据情况,把它们不断添加到上传列表中。在这里,信誉机制将会对此产生影响。

CUploadQueue的Process方法就相对简单了,那就是向上传队列中的所有客户端依次发送数据,而排队的客户端是不会得到这个机会的。另外它还需要完成关于上传方面的一些统计信息。

另外我们还需要注意在CUploadQueue的构造函数里面,创建了一个以100毫秒为间隔的定时器,这个定时器成为以上所有的Process所需要的基础。我们看它的UploadTimer就可以看出这一点。这里面充斥了各个类的Process方法的执行,其中包括以前我们提到的一些类,但是没有提到它们的Process方法,因为其过于简单,基本上就只是更新了一下要保存的信息。 

emule中代码量最大的类CUpDownClient

CUpDownClient 类的作用是从逻辑上表示一个其它的客户端的各种信息,它是emule中代码量最大的类。我们注意到,定义它的头文件是UpDownClient.h,但是却没有对应的CUpDownClient.cpp,而它的实现,都分散到 BaseClient.cpp,DownloadClient.cpp,PeerCacheClient.cpp,UploadClient.cpp和 URLClient.cpp中。

BaseClient.cpp中实现的是该类的一些基本的功能,包括基本的各种状态信息的获取和设置,以及按照要求处理和发送各种请求。在这里,逻辑实现和网络进行了区分,CUpDownClient类本身不从网络接受或者发送消息,它只是提供各种请求的处理接口,以及在发送请求时,构造好相应的Packet,并交给自己对应的网络套接字发出去。

DownloadClient.cpp中实现的是和下载相关的功能,它包括了各种下载请求的发送以及相应的数据的接收。另外还有一个A4AF的机制,它是emule中的一个机制,因为一个客户端在同一个时间内只能向另外一个客户端请求同一个文件。这样,对于很多个下载任务(CPartFile),有可能出现它们的源(即有该文件的客户端)有部分重叠的现象,而这时,如果其它下载任务正在从这个源下载,那么当前的下载任务就不能从这个源下载了。但是emule允许用户对其手动进行控制,如对下载任务的优先级进行区分,这样他就可以将一个源从另外一个下载任务那里切换过来。A4AF其实就是ask for another file的简称。

UploadClient.cpp中实现的是上传相关功能,即接受进来的下载请求,并且生成相应的文件块发送出去。

PeerCacheClient.cpp 实现的是和PeerCache相关的功能,PeerCache是一个由Joltid公司开发的技术,它可以允许你从ISP提供的一些快照服务器上快速得上传或者下载一些文件(或者是一部分),这个技术的好处是可以减少骨干网络的带宽消耗,将部分本来需要在骨干网上走的流量转移到ISP的内部。当然这个功能需要ISP的配合。如果发现ISP提供了这项服务的话,emule会利用它来减少骨干网的带宽消耗。

URLClient.cpp实现的功能是利用http协议对原有的emule协议进行包装,以便使它能够尽可能地穿越更多的网络的防火墙。 

emule常规部分小结

emule 中还有其它的很多类,它们使得emule的功能更加的强大和完善。有很多类在前面没有提到,但是不代表它没有作用。而且即时是前面提到的类也只是大体的介绍,它们之间互相配合的一些细节没有体现。但是这些细节应该已经可以通过对它们的大体的功能的了解而更加容易被把握。至于GUI的设计,它也最终是要对应到某个功能实现类的数据的。

对于emule中的通信协议只是大体得描述了一下它的数据包的格式,但是并没有详细得描述它的每一个 Opcode对应的包的意义,因为我认为这是没有必要的,在知道通信协议的格式以及处理它们的代码所在的位置后,可以很简单的通过追踪某条消息的前因后果把整个通信协议都分析出来。

这里再稍微提一下在emule中使用到的其它类及其功能。我们可以看到,如果单纯只是为了能够搜到以及下载到文件的话,有不少类是可以精简的,但是,正是由于它们的存在,使得emule的功能更加的完善。CIPFilter,IP地址过滤器,通过识别各种类型的 IP地址过滤信息,它能够把不希望连接的网络地址过滤掉,emule中所有需要连接网络的地方使用的都是统一的过滤数据。CWebServer能够在本地打开一个Web服务器,然后你可以通过浏览器来控制你的emule。CScheduler能够实现下载任务的定时下载。CPeerCacheFinder 为前面提到的PeerCache技术的主控制类。另外,emule还内置了一个IRC客户端,一个主要成员函数都为静态的 CPartFileConvert类,能够对其它版本的驴的下载文件进行转换。它甚至还提供了一个自动处理zip和rar的类 CArchiveRecovery。

Kademlia网络是emule中相当重要的一部分,因此特意把这一部分单独拿出来,把它放在这个小结之后进行描述。

继续阅读