首页 eMule学习心得

eMule学习心得

举报
开通vip

eMule学习心得 eMule学习心得 eMule的官方首页上写着:2002年 05月 13日 一个叫做 Merkur 的人,他不满意原始 eDonkey2000客户端并且坚信他能够做的更好,所以他开始制作。他聚集了其它开发人员在 他的周围,并且 eMule工程就此诞生。 eMule是一个典型的MFC程序,它的图形界面等,已经和MFC紧紧融合到了一起。因 此通常情况下它只能在 windows 平台下运行。有一些其它的工程,如 aMule 等,把它进行 了移植,因此跨平台的功能要强些。 其实还有另外一个叫做 ...

eMule学习心得
eMule学习 心得 信息技术培训心得 下载关于七一讲话心得体会关于国企改革心得体会关于使用希沃白板的心得体会国培计划培训心得体会 eMule的官方首页上写着:2002年 05月 13日 一个叫做 Merkur 的人,他不满意原始 eDonkey2000客户端并且坚信他能够做的更好,所以他开始制作。他聚集了其它开发人员在 他的周围,并且 eMule工程就此诞生。 eMule是一个典型的MFC程序,它的图形界面等,已经和MFC紧紧融合到了一起。因 此通常情况下它只能在 windows 平台下运行。有一些其它的工程,如 aMule 等,把它进行 了移植,因此跨平台的功能要强些。 其实还有另外一个叫做 xMule 的工程,不过现在已经人气快不行了。在 aMule 的主页 上可以看到 eMule移植到 linux平台下的一些历史,最早是有个叫做 lMule的工程,他使用 wxwidgets 来进行 eMule 的跨平台的移植,这个工程 2003 年就不再更新了,后来转变成为 xMule工程,它一度是 linux平台下 eMule的事实上的替代品。但是他们的程序员之间由于 理念不同,发生了内讧,导致 aMule 分裂出来,他们后来矛盾严重的时候曾经一度从理念 问 快递公司问题件快递公司问题件货款处理关于圆的周长面积重点题型关于解方程组的题及答案关于南海问题 上升到互相对对方进行人身攻击,并且曾经对对方的网站发动过 DDos。后来 aMule和 xMule就是两个完全不同的工程,xMule现在只有 HopeSeekr一个人在维护,基本上也没有 什么更新了。这一点不仅让人感慨。今年寒假的时候我曾经和 HopeSeekr进行过一些交流, 感觉他非常自信,经常拿着 aMule 的一部分代码来给我看,说你看看他们的代码这么这么 写,这简直就是一陀 xx 嘛,这种代码在某些情况下肯定会 Crash 掉嘛,相反,你看看我们 xMule的代码,这里是这样这样,肯定就不会有这种问题了。 eMule从 0.42版开始支持 Kad技术,这是一个非常重要的里程碑。Kad是一种 DHT的 协议,它可以使节点之间互相保留一些其它节点的联系信息,并且利用这样一个“关系网” 寻找到整个网络中的任何一个节点以及上面的资源,整个过程不需要任何中心服务器。因此 向当年搞 napster那样直接端掉中心服务器就搞跨 napster网络一样来对付 eMule的Kad网就 毫无作用了。0.42版是 2004年 2月 27日放出的,比 eDonkey2000 的 OverNet晚了将近一 年,但是它的Kad网络的规模却在迅速扩大。Overnet和 eMule中的Kad使用的都是Kademlia 结构,但是具体的消息报文的格式有区别,因此两个 DHT 网络并不能互相兼容。OverNet 直到现在,用户也仍然维持在十万左右,这是比较近的一篇文章写的。但是 eMule 的 Kad 网的规模有多大,目前却没有一个很准确的说法。由此也可以看出开源软件的力量。目前 aMule的 Kad网和 eMule的 Kad网是兼容的,xMule中还没有 Kad的支持。Kad协议的原 始 paper可以在我们实验室的机器上下到: http://bigpc.net.pku.edu.cn:8080/paper/new/by%20conference/IPTPS/IPTPS02/Kademlia%20A% 20Peer-to-Peer%20Information%20System%20Based%20on%20the%20XOR%20Metric%28IPT PS02%29.pdf 从 emule.cpp开始,顺便谈如何编译 emule eMule的代码结构非常合理。虽然代码量比较大,但是各个功能模块之间的划分都很合 理。从它的工程文件里面就可以看出这一点。eMule把表示功能的代码文件和表示界面的代 码文件分开了,Source Files和 Header Files是实现功能的代码的源文件和头文件,而 Interface Source和 Interface Header是实现图形界面的源文件和头文件。由于 eMule的代码量太大, 本系列将跳过图形界面的实现,着重分析 eMule 的功能实现部分的代码,而且功能实现方 面也主要挑主要的部分分析。本节将从 emule.cpp开始分析,引出 eMule中使用到的几个主 要的功能实现的类,并大体描述它们的作用。最后介绍一下如何在 VS2003下编译 eMule。 emule中还有不少模块实现的功能挺有用,我说的是在其它的程序里很有用,可以考虑在其 它程序中进行复用。 emule.cpp为类 CemuleApp的实现。因此在运行时,首先会运行 InitInstance进行一些初 始化的工作。从这个函数里面我们也可以第一次看出那些即将在整个程序中发挥作用的类 了。 最开始的时候是计算出程序常用的一些目录,如配置文件,日志文件等。接下来是 ProcessCommandline,它的作用有两方面,第一是确认该 eMule的运行方式,即命令行后面 有没有参数,第二是确认目前 eMule 是不是只有一个实例在运行。在一般的情况下,双击 eMule可执行文件是不会带参数的。但是通过点击链接或者打开关联文件的方式打开 eMule 则相当于带参数运行 eMule。通过在注册表里添加一些项目可以让一个程序和某种链接或者 某个后缀的文件产生关联。具体办法可以参见 OtherFunctions.cpp 中的 Ask4RegFix, BackupReg,RevertReg 三个函数的功能。ProcessCommandline 中通过创建带有名称的互斥 信号量来确认是否有其它的 eMule实例在运行。对于一个确定的名称,CreateMutex只能创 建一个互斥信号量。因此通过该信号量是否创建成功就可以知道是否有其它 eMule 实例运 行。如果有的话,而且又是带参数的那种模式,那么直接把这个参数使用 Windows 的消息 机制发给那个窗口即可,接下来的代码无非就是如何找到另外一个叫"eMule"的家伙以及给 它发个什么消息。pstrPendingLink是一个全局变量,表示将要被处理的命令行参数。它将会 在初始化完成后一段时间后被处理。 下面两个比较重要的类是 CPreferences 和 CStatistics。前者掌握着程序的大部分配置数 据,后者则进行各种统计。它们的特点都是有很多的成员变量,而且还是静态的,这种方式 可以保证它们的唯一性,而且把这些变量统一到一个类管理。但是实际上并不需要了解每个 变量的含义。thePrefs和 theStats是这两个类的唯一的实例。 在处理完其它一些事情,包括创建图形界面对象 CemuleDlg后,接下来可以看到一排一 排的创建新对象的语句。这些类将会实现 eMule 程序运行的主要功能,后面的系列将详细 分析。 最后描述一下如何在 VS2003里编译 eMule,由于 eMule中用到了一些其它的库,因此 官方在提供 eMule 的源代码下载的同时如果也提供这些库的下载会使源码包变得很大。因 此 eMule 选择了让开发者去那些库的官方网站下载它们的方式。一般来说,编译这种工程 文件,很重要的地方是要保持各个库和主程序编译参数的一致性。这些编译参数中,最主要 的参数有三个,字符集(多字节/Unicode),调试/发行,单线程/多线程,这样排列组合一下就 有八个版本了。因此编译的时候如果不注意,就会出现和程序设计无关的错误。 eMule0.47a解压后自带 id3lib库,还需要下载以下的库: zlib: http://www.gzip.org/zlib/ ResizableLib: http://sourceforge.net/projects/resizablelib/ Crypto++: http://www.eskimo.com/~weidai/cryptlib.html pnglib: http://www.libpng.org/pub/png/libpng.html 下载它们,解压它们,编译它们。编译的时候注意要和 eMule的工程的参数一致:字符 集为 Unicode,支持多线程安全,调试版或者是发行版可以根据需要选择,但是也要和 eMule 的工程参数一致。这些库的解压包里通常都能找到 VC的工程文件,但是版本低一些,直接 转化就可以了。另外建议编译这些库的时候都选择生成静态库,不要生成动态的库,这样最 后生成的可执行文件就可以自己运行了。编译完这些库,包括从其它地方下载的和 eMule 自带的 id3lib库和 CxImage库后,就可以开始编译 emule了。而编译 emule也无非就是注意 让它能够在编译的时候找到所有的头文件,以及在链接的时候能够找到所有的库。在链接的 时候能够找到所有的库可以通过修改工程文件里面的属性 ->配置属性 ->链接器 ->输入-> 附加依赖项来完成。但是能够找到所有的头文件反而需要一些技巧了。由于 emule的代码中 对于这些库的头文件的包含,在一定程度上限定了那些库的路径和 emule的工程的路径的相 对位置,因此需要把那些解压过的库的目录移到一些合适的地方,有时还需要给这些目录改 个名称。 emule中最重要的几个基础设施 eMule中要读取的配置文件数量较多,每种配置文件都是自己定义的格式,为了方便读 取和存储这些文件,eMule中有一个很重要的基础设施类来复制这些文件操作,它能够很方 便得处理一些常用数据类型的读写,并且带有一定的安全保护机制。这项基础设施在 SafeFile.cpp 和 SafeFile.h 中实现。在 kademlia\io 目录下有这项功能的另外一项实现。它们 实现的功能基本上相似,但是 kademlia\io 目录下的版本实现的时候多了一个以 Tag 作为单 位进行读写的功能。这些实现中和另外一项基础设施,那就是字符串转化密切相关。 StringConversion.cpp和 StringConversion.h是 eMule中专门复制各类字符串转化的基础设施, 什么 Unicode 啊,多字节流啊,或者是 UTF-8 之类的,在这里转化全部都不是问题。关于 字符串转化,个人推荐尽量使用 Unicode的宽字符,这样可以最大程度得避免乱码。 SafeFile.cpp 或者 kademlia\io 目录下的实现都有这样的特点,那就是把数据操作的行为 和数据操作的对象分割开来。它们都定义了一个抽象的数据操作的基类(在 SafeFile.cpp中是 CFileDataIO,在 kademlia 目录下是 DataIO.cpp 实现的 Kademlia::CDataIO),这个类中只负 责实现在逻辑上操作一项数据的行为,例如,要读取出一个 32位的整型,那么就是读出四 个字节到一个整型数值的地址中,要读取或者写入其它类型的数据用的是类似的方法。但是 这个类把物理上进行数据操作的方法全部都声明为纯虚函数,即读出多少个字节,写入多少 个字节这样的。有了这样一个基类,就可以非常方便得在它上面进行重载,把这些纯虚函数 定义为向某块内存中进行读写的操作,就能很方便得将较为复杂的数据序列化到一块连续的 内存,而如果这些纯虚函数是向文件读写的操作,那么自然就可以很方便得用来读写各种格 式比较奇怪的自己定义的配置文件了。 这些类要读取的数据对象通常有这些,各种整型,字符串,以及 Tag类型。整型读写起 来比较简单,从 1个字节的,2个字节的到 4个,8个或者 16个字节类型的数据读写方法都 比较类似。这里要稍微提一下 16 个字节的那种,16 个字节是 128 位,是 eMule 中的 Kad 网的随机生成的 ID的长度,也是 eMule中常用的MD4的 hash算法生成的结果的长度。通 常用来直接存取整个的这样的一个 ID。在 kademlia\utils目录下的 UInt128.cpp实现了一个表 示 128位的整数的类,功能十分完善,可以进行一些算术操作,并且可以进行比较,这样为 它以后作为 key出现在 hash表中打下了基础。仔细学习 UInt128.cpp中的代码实现可以学到 很多在编写这种自定义的数据对象类型时应该注意的问题。 数据操作中另外一项很重要的操作是字符串,总的原则是先写一个长度,再写内容。但 是到具体的操作的时候就需要注意这些细节了,如长度是写 4个字节还是两个字节,字符串 的内容要不要用 UTF-8进行编码。这些操作就需要和 StringConversion.cpp紧密合作了。其 实后者的字符串转化函数很多也是调用 ATL 的相关函数,只是在外面再包上一层 MFC 的 CString。 在 kademlia\io\DataIO.cpp中实现的CDataIO中,还另外实现了按照Tag进行读写的功能。 这在网络上交换共享文件的元信息非常重要,通常一个文件的元信息就可以分解成很多的 Tag,如"文件名=xxx","文件长度=xxx"等等。也就是说,一个 Tag 就是表示某项属性等于 某个值这样一个事实。在 Opcodes.h这个文件中定义了很多的代码,其中就有很多常见的 Tag 的属性名称。CDataIO类中存储 Tag的属性名都是先存一个字节的类型,再存名称,最后按 照类型存值。 eMule中的这几项基础设施都是编写得比较好的,可以很方便得拿出来复用。像字符串 编码的处理和具有一定数据结构的文件 IO 操作在很多地方都会很有用。eMule 中这些类的 实现基本上复制到其它的工程文件中只要稍微修改一下很快就能使用。以后我们还将看到 eMule中很多其它很有用的基础设施。 对自己的资源要了如指掌,CKnownFileList类的作用 emule 作为一个文件共享方面的程序,首先要对自己共享的所有的文件的信息都十分清 楚,类 CKnownFileList 的作用就是这样的,它在 emule.cpp 中随着 cmuleapp 类创建的时候 被创建。 CKnownFileList类使用了 MFC的 CMap类来维护内部的 hash表,这也可以看出 emule 和 MFC 的关系确实非常紧密。这里如果用 STL 的 map 其实也是可以的。它内部维护了一 个已知的文件的列表和取消了的文件列表。这些 hash表的关键字都是文件的 hash值。这样 能够判断出文件名不同而内容相同的文件,而一般要让不同内容的文件有相同的 hash 值是 非常困难的,这也是 hash 函数它设计的初衷。因此除非是碰上王小云教授这样的牛人,我 们基本上可以认为,两个文件 hash 值相同就代表了它们内容相同。再来看 CKnownFileList.cpp,这个文件其实并不长,因为管理一个列表确实不需要太多种类的操作, 如果对于每个具体的文件有一个很强大的类来处理它的话。而这里确实有,它就是 CKnownFile。有了这么一个类,我们就可以看到,CKnownFileList 类所需要做的工作就是 能够根据一些信息查找到对应的 CKnownFile类,能够复制其它的列表中的信息,能够把所 有的这些信息存成文件,然后下次 emule运行的时候能够把这些信息快速恢复出来,最重要 的是能够在完成以上工作的情况下不造成内存泄漏。 CKnownFile 类就是一个专门关注某个特定文件的信息的类,它仍然有其基类 CAbstractFile。但是它和 CAbstractFile类的主要区别就是 CAbstractFile类只有基本的信息存 取的功能,而 CKnownFile 能够主动的生成这些信息,例如,给一个文件的路径给 CKnownFile,它能够主动地去获取和这个文件有关的一切信息,并且把它保存在自己的成 员变量里(CreateFromFile)。CKnownFile.cpp文件看上去比较长,是因为它做的工作比较多, 现在版本的 emule 中,除了对某个文件进行全文 hash 以外,还采用了 BT 的方式,进行分 块 hash,这样在传输文件的时候,即使发生出错的情况,也可以不必重传整个文件,而只 是重传有错误的那块,这种机制叫做高级智能损坏处理 (AICH,Advanced Intelligent Corruption Handling),这个机制以后再继续分析。 CKnownFile把读到的文件信息都保存成一个一个的 Tag。它在运行中会尽量得获取更多 的文件信息,例如,对于媒体类型的文件,它能够调用 id3lib库来获取诸如作者,唱片发行 年代,风格等 tag信息。如果是视频媒体文件,它还会去抓图(功能实现:CFrameGrabThread)。 CKnownFile还能够随时掌握目前该文件的下载情况(内部有个 CUpDownClient的列表), 当然,还会根据要求序列化和反序列化自己,LoadFromFile和WriteToFile都以 CFileDataIO 为参数,这样方便 CKnownFileList保存和读取它的列表中的所有文件的信息。 分块机制--正确传输资源的保证 为了加快内容分发的速度,分块处理是一种简单有效的方法。emule 中对每个文件都进 行了分块处理。另外分块还有一个好处就是如果保留了每一分块的 hash 值,就能在只下载 到文件的一部分时判断出下载内容的有效性。emule在获取每个共享文件的信息时,就对它 进行了分块处理,因此如果要知道 emule 中的分块处理和恢复机制,看 CKnownFile::CreateFromFile函数的实现就行了。 这个函数中牵涉到的和分块处理以及 hash 计算相关的类都在 SHAHashSet.cpp 和 SHAHashSet.h中。下面介绍其中几个主要的类: CAICHHash 类只负责一块 hash 值,提供两个 CAICHHash 类之间的直接赋值,比较等 基本操作。CAICHHashAlgo 是一个 hash 算法的通用的接口,其它 hash 算法只要实现这种 接口都能使用,这样,可以很方便得使用不同的 hash算法来计算 hash值。CAICHHashTree 则是一个树状的 hash 值组织方式,它有一个左子树和右子树成员变量,类型是指向 CAICHHashTree的指针,这是一个典型的实现树状结构的方法。CAICHHashSet中包含了一 个 CAICHHashTree 类型的变量,它直接向 CKnownFile 负责,代表的是一个文件的分块信 息。 SHAHashSet.h文件的开始的注释部分向我们解释了它的分块的方式。这里要用到两个常 量 9728000和 184320,它们分别是 9500k和 180k。这是 emule中两种不同粒度的分块方式, 即首先把一个很大的文件分割成若干个 9500k的块,把这些块组织成一颗树状的结构,然后 每一个这样的块又分解成若干个 180k的块(52块,再加一个 140k的块),仍然按照树状的结 构组织起来。最后总的结构还是一颗树。 CKnownFile::CreateFromFile 方法是在读取目标文件的内容时,逐步建立起这样一颗树 的。CAICHHashTree::FindHash能够根据读取到的目标文件的偏移量和下一块的大小,来找 出对应的树枝节点(就是一个指向 CAICHHashTree 的指针)。如果有必要的话,还会自动创 建这些树枝节点。因此在进行分块操作的时候,把文件从头到尾读一边,整个 CAICHHashTree就建立起来了,对应的分块 hash值也赋值好了。最后我们还需要注意的就 是 CKnownFile 类中的 hashlist 变量。就是说它还单独保留直接以 9728000 字节为单位的所 有分块的MD4算法的 hash值。这样对于一个文件就有了两套分块验证的机制,能够适应不 同场合。 网络基础设施--网络基础设施的基础设施 MFC中已经有一些网络基础设施类,如 CAsyncSocket等。但是 emule在设计中,为了 能够更加高效得开发网络相关的代码,构建了另外的一些类作为基础设施,这些基础设施类 的代码也有很高的复用价值。 首先是 CAsyncSocketEx类。AsyncSocketEx.h中对这个类的特点已经给出了一定的 说明 关于失联党员情况说明岗位说明总经理岗位说明书会计岗位说明书行政主管岗位说明书 。 它完全兼容 CAsyncSocket类,即把应用程序中所以的 CAsyncSocket换成 CAsyncSocketEx, 程序仍然能够和原来的功能相同,因此在使用上更加方便。但是在这个基础上,它的效率更 高,主要是在消息分发机制上,即它处理和 SOCKET相关的消息的效率要比原始的MFC的 CAsyncSocket类更高。另外,CAsyncSocketEx类支持通过实现 CAsyncSocketExLayer类的 方式,将一个 SOCKET分成若干个层,从而可以很方便得实现许多网络功能,如设置代理, 或者是使用 SSL进行加密等。 另外还有 ThrottledSocket.h中定义的 ThrottledControlSocket类和 ThrottledFileSocket类, 这两个类只定义了两个接口。任何其它的网络套接字类如果想实现限速的功能,只需要在其 默认的发送函数(如 Send 或 Sendto)中不发送数据而是把数据缓存起来,然后在实现 ThrottledControlSocket 或者 ThrottledFileSocket 接口中的 SendFileAndControlData 或 SendControlData 方法时才真正把数据发送出去,这样就能实现上传限速,而这也是需要 UploadBandwidthThrottler类进行配合,UploadBandwidthThrottler是一个WinThread的子类, 平时单独运行一个线程。下一次会详细描述它是如何控制全局的上传速度的。 网络基础设施--全局限速器 UploadBandwidthThrottler UploadBandwidthThrottler 是 emule 中使用的全局的上传限速器。它继承了 CWinThread 类,且在该类被创建的时候,就新创建一个线程开始单独运行。在该类被析构时也会自动停 止相应的线程。这个线程的目标函数就是 RunProc,然后为了避免在 RunProc函数不能使用 this指针的情况,它使用了 RunInternal来实际完成工作线程的工作。在 emule中,还有另外 一个类 LastCommonRouteFinder有类似的结构。 UploadBandwidthThrottler 中保存了若干的套接字(Socket)队列,这些队列的处理方式略 有不同。在 标准 excel标准偏差excel标准偏差函数exl标准差函数国标检验抽样标准表免费下载红头文件格式标准下载 队列(m_StandardOrder_list)里面排队的都是实现了 ThrottledFileSocket接口的 类,通常这些类能够传输文件内容也可以传输控制信息。而其它四个队列都是实现 ThrottledControlSocket接口的类的队列,在这些队列中的类主要以传输控制信息为主。这四 个队列为临时高优先级,临时普通优先级,正式高优先级,正式普通优先级。和把套件字直 接添加到普通队列(AddToStandardList)不同,QueueForSendingControlPacket把要添加到队列 的套接字全部添加到两个临时队列。根据它们的优先级添加到普通的临时队列。在 RunInternal的大循环中,临时队列中的项目先被移到普通队列中,然后再进行处理。 UploadBandwidthThrottler使用了两个临界区,两个事件。pauseEvent是用来暂停整个大 循环的动作的。而 threadEndedEvent是标志整个线程停止的事件。sendLocker是大循环中使 用的主要的临界区,而 tempQueueLocker是为两个临时队列额外添加的锁,这样可以一边发 送已有队列中的套界字要发送的数据,一边把新的套接字加到队列中。 UploadBandwidthThrottler 的 RunInternal 中的大循环是该工作线程的日常操作。这个大 循环中做了以下事情,计算本次配额,即本次循环中能够发送多少字节,好安排调度,计算 本次循环应该睡眠多少时间,然后进行相应的睡眠,从而进行限速。操作控制信息队列,发 送该队列中的数据,注意,控制队列中的套接字 (m_ControlQueueFirst_list 和 m_ControlQueue_list)只使用一次就离开队列。而标准队列中的套接字不会这样。在一轮循环 结束后,如果还有没有用完的发送数据的配额,则会有部分配额保存到下一轮。 网络基础设施--emule套接字 CEMSocket CEMSocket是 CAsyncSocketEx和 ThrottledFileSocket的子类,它把若干功能整合到了一 起,因此可以作为 emule 使用起来比较方便的套接字。例如它可以很方便得指定代理,把 CAsyncSocketEx 中的创建一个新的代理层并且添加到列表中的功能对外屏蔽了。另外它可 以分出状态,如当前是否在发送控制信息等。 CEMSocket 中我们需要仔细考察的是它的 SendControlData 和 SendFileAndControlData 方法。如前所述,这些方法是用来和 UploadBandwidthThrottler 进行配合,以便完成全局的 限速功能的。它的功能应该是按照 UploadBandwidthThrottler 的要求,在本次轮到它发送数 据时发送指定数量的字节数。因此,应用程序的其它部分在使用 CEMSocket 时,如果要达 到上传数据限速的目的,不应该直接调用标准的 Send 或者 SendTo 方法,而是调用 SendPacket。这里就有了另外一个结构 Packet,它通常包含一个 emule协议中完整的包,例 如有协议的头部数据等,还内置了 PackPacket和 UnPackPacket方法,可以自行进行压缩和 解压的功能。SendPacket 把要发送的 Packet 放到自己的队列中,这个队列也有两个,控制 信息包队列,和标准信息包队列。如果有必要,把自己加入到 UploadBandwidthThrottler 的 队列中。 我们注意到 CEMSocket的 SendControlData和 SendFileAndControlData方法其实都是调 用 自 己 的 另 一 个 重 载 的 Send 方 法 。 而 且 我 们 也 已 经 知 道 这 个 方 法 是 在 UploadBandwidthThrottler的工作线程中的大循环中被调用的,而这个 Send方法的内容本身 也是一个大循环,但是意义很明了,就是在不超过自己本次发送的配额的情况下,把自己的 包队列中的包取出来,并且发出去。同样,这里也用到了一个临界区,它是为了保证从包队 列中取出包来发送和把包往队列中放的操作是互斥的。因此,如果把它和 UploadBandwidthThrottler 结合起来,我们就看到了一个两层的队列,即所有的套接字组成 了一个发送队列,在 UploadBandwidthThrottler 的控制下保证了对速度的限制,而每个套接 字即将发送的数据包又组成了一个队列,保证了每次进行数据发送的时候都会满足 UploadBandwidthThrottler的要求。 搜索信息集-CSearchList CSearchList是 emule中的搜索列表,掌管 emule中所有的搜索请求。CSearchFile是这个 列表中的元素,代表了一次搜索的相关信息。它们的关系和之前描述的已知文件和已知文件 列表有一些类似的地方。CSearchList的主要任务就是对其一个叫做 list的类型为 CSearchFile 列表的内部变量进行维护,提供很方便得往这个列表中添加,删除,查询,变更等操作的接 口。另外,每一个搜索都有一个 ID,是一个 32位的整数。CSearchList中记录了每个搜索目 前搜到的文件个数和源的个数(m_foundFilesCount和 m_foundSourcesCount)。 CSearchFile 是 CAbstractFile 的另一个子类(CKnownFile 也是),它保存了某个文件和搜 索相关的信息,而不是这个文件本身的信息(这些信息在 CAbstractFile中已经包括了),这些 和搜索有关的信息就是都在哪些机器上有这个文件,以及哪个服务器上搜到的这个文件。甚 至还可以向搜索文件添加预览。在这个类的定义中嵌套定义了两个简单的结构 SServer 和 SClient,表示了该搜索文件的可能来源,服务器或者其它客户端。m_aClients和 m_aServers 是这两个简单结构的一个数组,CSearchFile 自然也提供了对这个数组的操作的接口,方便 CSearchList使用。 CSearchList 对外提供了搜索表达的接口,即每当有一个新的搜索提交时 CSearchList::NewSearch 会建立一个新的搜索项,但是此时还没有任何对应的搜索文件,因 此只是在文件个数和搜索 ID的对应表(m_foundFilesCount和m_foundSourcesCount)中建立新 的项目。另外当有搜索结果返回时 ProcessSearchAnswer或 ProcessUDPSearchAnswer能够对 返回的包直接做处理,创建相应的搜索文件信息 CSearchFile对象,并加入到自己的列表中。 当然,要把重复的搜索结果去除,发现同一个 hash 的文件的多个源时也会给它们建立一个 二级列表(CSearchFile::m_list_parent)。现在我们可以看出,CSearchList只负责和搜索有关的 信息的储存和读取,本身并不进行搜索。 服务器信息集-CServerList 尽管目前有了 Kad网络,但是使用服务器来获取各个 emule用户的共享文件列表仍然是 emule中主要的资源获取方式。CServerList就是 emule中负责管理服务器列表的类。和前面 若干列表类结构类似,CServerList 需要对外提供列表的增加,删除,查找,修改等接口。 在 CServerList 中,每个服务器的信息是一个 CServer 类。和搜索信息不一样,但是和已知 文件列表一样,服务器的信息列表是需要长期保留的,因此 CServerList 和 CKnownFileList 类一样提供了把它所包含的所有信息保存到一个文件中,以及从这个文件中读回其信息的功 能。 CServer中的结构比较简单,只需要保留服务器的各种信息即可。它可以通过 IP地址和 端口来创建,也可以通过一个简单的结构 ServerMet_Struct来创建,其中后者是用来直接从 文件中读取的。该结构仅仅包含 IP地址和端口以及属性的个数,CServer中其它的属性在保 存到文件中时,均采用 Tag方式保存。 CServerList除了提供通常的 CServer信息外,还提供一些统计信息诸如所有的服务器的 用户数,共享的文件数等。这些统计信息也是基于每个单独的 CServer的相关信息计算出来 的。 emule的通信协议-一些基本的约定 接下来将不可避免得要碰到 emule 的协议。emule 的通信协议格式设计成一种便于扩充 的格式。对于 TCP连接来说,连接中的数据流都能够划分成为一个一个的 Packet,CEMSocket 类中就完成了把接收到的数据划分成 Packet 这一工作。但是具体的对于每个 Packet 进行处 理的工作被转移到它的子类中进行。CEMSocket 类的两个子类 CServerSocket 和 CClientReqSocket所代表的 TCP连接就分别是客户端和服务器之间的 TCP连接以及客户端 之间的 TCP连接。在数据流中的第一个字节代表的是通信的协议簇代码,如 0xE3为标准的 edonkey 协议,0xE4 为 kademlia 协议等等。接下来的四个字节代表包内容的长度,所有的 包都用这种方式发送到 TCP 流中,就可以区分出来了。另外每个包内容中的第一个字节为 opcode,即在确定了某个具体协议后,这个 opcode确定了这个包的具体含义。 对于走 UDP协议的包,处理起来更加得简单,因为 UDP本来就是以一个包一个包作为 单位在网络上流传的,因此不需要在包的内容中再包含表示长度的字段。每个 UDP包的第 一个字节是协议簇代码,其它内容就是包的内容。CClientUDPSocket 类负责处理客户端和 客户端之间的 UDP包,而 CUDPSocket类负责处理客户端和服务器之间的 UDP包。另外还 有个 Kademlia::CKademliaUDPListener类,专门处理和 Kademlia协议相关的 UDP包。 最后说一下 Packet类,这个类以前只是提到过。它是 emule的通信协议的最小单位。我 们可以看出,它的构造函数有多个版本,这也是为了可以用不同的方式来创建 Packet。例如 只包含一个头部信息的缓冲区,或者只是指定协议簇代码等。而且它内部实现了压缩和解压 的方法,该方法直接调用 zlib库中的压缩方法,可以减少数据的传输量。这里要注意一点的 就是压缩的时候协议簇代码是不参与压缩的,压缩完毕后会更换协议簇代码,例如代码为标 准 edonkey协议 0xE3 的包在压缩后,协议代码就变成 0xD4 了,这里进行协议代码变化是 为了使接受方能够正确识别并且进行相应的解压操作。 emule的通信协议-客户端和服务器之间的通信概述 客户端和服务器之间的所有通信由类 CServerConnect掌握。CServerConnect本身不是套 接字的子类,但是它的成员变量 CServerSocket 类型的 connectedsocket 是。CServerConnect 内部有一列表,可以保存若干 CServerSocket类型的指针。但是这并不说明它平时连接到很 多服务器上。它只是可以同时试图连接到若干个服务器上,这只是因为连接到服务器上的行 为不一定能成功。 CServerSocket类是 CEMSocket的子类,它比 CEMSocket要多保存一些状态,比如当前 的服务器连接状态。它同时还保留它当前所连接的服务器的信息。通过分析 CServerSocket::ProcessPacket 就可以直接把 emule 客户端和服务器之间的通信协议理解清 楚 , 这 里 是 服 务 器 发 回 的 包 。 TCP 连 接 建 立 后 的 第 一 个 包 是 在 CServerConnect::ConnectionEstablished中发出的,即向服务器发出登陆信息。如果登陆成功, 则能够从服务器处获取自己的 ID,这是一个 32 位的长整数。如果这个数小于 16777216, 那么我们称它为 LowID。具有 LowID的客户端通常情况下其它客户端将不能直接连接它。 得到 LowID的原因比较多,例如当自己处于 NAT的后端的时候。获取自己的 ID后将会向 服务器发送自己的共享文件列表,这一动作由共享文件列表类 CSharedFileList来完成。 其它类型包没有必要全部都列出来,以后可以通过在分析其它部分时,因为牵涉到往服 务器发送或者接受数据的时候再进行相应的分析。 emule的通信协议-客户端和客户端之间的通信概述 客户端和客户端之间的 TCP通信由 CListenSocket和 CClientReqSocket完成。这也是提 供网络服务的应用程序的典型写法。其中 CListenSocket只是 CAsyncSocketEx的子类,只负 责监听某个 TCP端口。它只是内部有一个 CClientReqSocket类的列表。而 CClientReqSocket 是 CEMSocket的子类,因此它能够自动完成 emule的 packet识别工作。它有 ProcessPacket 和 ProcessExtPacket来处理客户端和客户端之间的包,其中前者是经典的 eDonkey协议的包, 后者是 emule扩展协议的包。 CListenSocket和CClientReqSocket类之间的关系和前面分析的列表类和它对应的成员类 的关系是相似的,CListenSocket 提供对自身的 CClientReqSocket 列表中的元素的增加,查 询,删除等操作。同时也维护关于这些成员的一些统计信息。我们注意到 CListenSocket 在 其构造函数中就把自己添加到 CListenSocket 类(theApp.listensocket,该类的唯一实际示例) 的列表中。 CClientReqSocket类和 CUpDownClient类之间存在着对应关系。它们都表示了另外一个 客户端的一些信息,但是 CClientReqSocket类主要侧重在网络数据方面,即负责两边的互相 通信,而 CUpDownClient 类负责的是从逻辑上对网络另一边的一个客户端进行表达。 CUpDownClient类代码很长,以后再说。 emule中的信誉机制 信誉机制在 P2P系统中有非常重要的作用。为了使用户更加愿意共享自己的资源,需要 有一些机制能够让对整个 P2P 系统贡献更大的用户有更多的激励。在 emule 中,激励机制 的设计 方案 气瓶 现场处置方案 .pdf气瓶 现场处置方案 .doc见习基地管理方案.doc关于群访事件的化解方案建筑工地扬尘治理专项方案下载 是 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学习心得】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_323994
暂无简介~
格式:pdf
大小:263KB
软件:PDF阅读器
页数:0
分类:互联网
上传时间:2010-11-19
浏览量:17