📄 emule源代码解析-1.txt
字号:
pnglib:
http://www.libpng.org/pub/png/libpng.html
下载它们,解压它们,编译它们。编译的时候注意要和eMule的工程的参数一致:字符集为Unicode,支持多线程安全,调试版或者是发行版可以根据需要选择,但是也要和eMule的工程参数一致。这些库的解压包里通常都能找到VC的工程文件,但是版本低一些,直接转化就可以了。另外建议编译这些库的时候都选择生成静态库,不要生成动态的库,这样最后生成的可执行文件就可以自己运行了。编译完这些库,包括从其它地方下载的和eMule自带的id3lib库和CxImage库后,就可以开始编译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中很多其它很有用的基础设施。
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保存和读取它的列表中的所有文件的信息。
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -