• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            牽著老婆滿街逛

            嚴(yán)以律己,寬以待人. 三思而后行.
            GMail/GTalk: yanglinbo#google.com;
            MSN/Email: tx7do#yahoo.com.cn;
            QQ: 3 0 3 3 9 6 9 2 0 .

            轉(zhuǎn)貼: wolfenstein工作室-eMule源代碼學(xué)習(xí)心得

            1, eMule源代碼學(xué)習(xí)心得(1):eMule代碼的總體風(fēng)格和其它相關(guān)工程

            eMule的官方首頁(yè)上寫著:2002年05月13日 一個(gè)叫做 Merkur 的人,他不滿意原始eDonkey2000客戶端并且堅(jiān)信他能夠做的更好,所以他開始制作。他聚集了其它開發(fā)人員在他的周圍,并且eMule工程就此誕生。

            eMule是一個(gè)典型的MFC程序,它的圖形界面等,已經(jīng)和MFC緊緊融合到了一起。因此通常情況下它只能在windows平臺(tái)下運(yùn)行。有一些其它的工程,如aMule等,把它進(jìn)行了移植,因此跨平臺(tái)的功能要強(qiáng)些。

            其 實(shí)還有另外一個(gè)叫做xMule的工程,不過現(xiàn)在已經(jīng)人氣快不行了。在aMule的主頁(yè)上可以看到eMule移植到linux平臺(tái)下的一些歷史,最早是有個(gè) 叫做lMule的工程,他使用wxwidgets來進(jìn)行eMule的跨平臺(tái)的移植,這個(gè)工程2003年就不再更新了,后來轉(zhuǎn)變成為xMule工程,它一度 是linux平臺(tái)下eMule的事實(shí)上的替代品。但是他們的程序員之間由于理念不同,發(fā)生了內(nèi)訌,導(dǎo)致aMule分裂出來,他們后來矛盾嚴(yán)重的時(shí)候曾經(jīng)一 度從理念問題上升到互相對(duì)對(duì)方進(jìn)行人身攻擊,并且曾經(jīng)對(duì)對(duì)方的網(wǎng)站發(fā)動(dòng)過DDos。后來aMule和xMule就是兩個(gè)完全不同的工程,xMule現(xiàn)在只 有HopeSeekr一個(gè)人在維護(hù),基本上也沒有什么更新了。這一點(diǎn)不僅讓人感慨。今年寒假的時(shí)候我曾經(jīng)和HopeSeekr進(jìn)行過一些交流,感覺他非常 自信,經(jīng)常拿著aMule的一部分代碼來給我看,說你看看他們的代碼這么這么寫,這簡(jiǎn)直就是一陀xx嘛,這種代碼在某些情況下肯定會(huì)Crash掉嘛,相 反,你看看我們xMule的代碼,這里是這樣這樣,肯定就不會(huì)有這種問題了。

            eMule從0.42版開始支持Kad技術(shù),這是一個(gè)非常重要 的里程碑。Kad是一種DHT的協(xié)議,它可以使節(jié)點(diǎn)之間互相保留一些其它節(jié)點(diǎn)的聯(lián)系信息,并且利用這樣一個(gè)“關(guān)系網(wǎng)”尋找到整個(gè)網(wǎng)絡(luò)中的任何一個(gè)節(jié)點(diǎn)以及 上面的資源,整個(gè)過程不需要任何中心服務(wù)器。因此向當(dāng)年搞napster那樣直接端掉中心服務(wù)器就搞跨napster網(wǎng)絡(luò)一樣來對(duì)付eMule的Kad網(wǎng) 就毫無作用了。0.42版是2004年2月27日放出的,比eDonkey2000的OverNet晚了將近一年,但是它的Kad網(wǎng)絡(luò)的規(guī)模卻在迅速擴(kuò) 大。Overnet和eMule中的Kad使用的都是Kademlia結(jié)構(gòu),但是具體的消息報(bào)文的格式有區(qū)別,因此兩個(gè)DHT網(wǎng)絡(luò)并不能互相兼容。 OverNet直到現(xiàn)在,用戶也仍然維持在十萬(wàn)左右,這是比較近的一篇文章寫的。但是eMule的Kad網(wǎng)的規(guī)模有多大,目前卻沒有一個(gè)很準(zhǔn)確的說法。由 此也可以看出開源軟件的力量。目前aMule的Kad網(wǎng)和eMule的Kad網(wǎng)是兼容的,xMule中還沒有Kad的支持。Kad協(xié)議的原始paper可 以在我們實(shí)驗(yàn)室的機(jī)器上下到:

            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%28IPTPS02%29.pdf

            2. eMule源代碼學(xué)習(xí)心得(2):從emule.cpp開始,順便談如何編譯emule

            先說一聲抱歉,因?yàn)榍皟商靹偦氐郊抑校菹⒘艘幌拢赃@兩天沒有更新。今天繼續(xù)昨天的話題。

            eMule 的代碼結(jié)構(gòu)非常合理。雖然代碼量比較大,但是各個(gè)功能模塊之間的劃分都很合理。從它的工程文件里面就可以看出這一點(diǎn)。eMule把表示功能的代碼文件和表 示界面的代碼文件分開了,Source Files和Header Files是實(shí)現(xiàn)功能的代碼的源文件和頭文件,而Interface Source和Interface Header是實(shí)現(xiàn)圖形界面的源文件和頭文件。由于eMule的代碼量太大,本系列將跳過圖形界面的實(shí)現(xiàn),著重分析eMule的功能實(shí)現(xiàn)部分的代碼,而且 功能實(shí)現(xiàn)方面也主要挑主要的部分分析。本節(jié)將從emule.cpp開始分析,引出eMule中使用到的幾個(gè)主要的功能實(shí)現(xiàn)的類,并大體描述它們的作用。最 后介紹一下如何在VS2003下編譯eMule。emule中還有不少模塊實(shí)現(xiàn)的功能挺有用,我說的是在其它的程序里很有用,可以考慮在其它程序中進(jìn)行復(fù) 用。

            emule.cpp為類CemuleApp的實(shí)現(xiàn)。因此在運(yùn)行時(shí),首先會(huì)運(yùn)行InitInstance進(jìn)行一些初始化的工作。從這個(gè)函數(shù)里面我們也可以第一次看出那些即將在整個(gè)程序中發(fā)揮作用的類了。

            最 開始的時(shí)候是計(jì)算出程序常用的一些目錄,如配置文件,日志文件等。接下來是ProcessCommandline,它的作用有兩方面,第一是確認(rèn)該 eMule的運(yùn)行方式,即命令行后面有沒有參數(shù),第二是確認(rèn)目前eMule是不是只有一個(gè)實(shí)例在運(yùn)行。在一般的情況下,雙擊eMule可執(zhí)行文件是不會(huì)帶 參數(shù)的。但是通過點(diǎn)擊鏈接或者打開關(guān)聯(lián)文件的方式打開eMule則相當(dāng)于帶參數(shù)運(yùn)行eMule。通過在注冊(cè)表里添加一些項(xiàng)目可以讓一個(gè)程序和某種鏈接或者 某個(gè)后綴的文件產(chǎn)生關(guān)聯(lián)。具體辦法可以參見OtherFunctions.cpp中的Ask4RegFix,BackupReg,RevertReg三個(gè) 函數(shù)的功能。ProcessCommandline中通過創(chuàng)建帶有名稱的互斥信號(hào)量來確認(rèn)是否有其它的eMule實(shí)例在運(yùn)行。對(duì)于一個(gè)確定的名稱, CreateMutex只能創(chuàng)建一個(gè)互斥信號(hào)量。因此通過該信號(hào)量是否創(chuàng)建成功就可以知道是否有其它eMule實(shí)例運(yùn)行。如果有的話,而且又是帶參數(shù)的那 種模式,那么直接把這個(gè)參數(shù)使用Windows的消息機(jī)制發(fā)給那個(gè)窗口即可,接下來的代碼無非就是如何找到另外一個(gè)叫"eMule"的家伙以及給它發(fā)個(gè)什 么消息。pstrPendingLink是一個(gè)全局變量,表示將要被處理的命令行參數(shù)。它將會(huì)在初始化完成后一段時(shí)間后被處理。

            下面兩個(gè)比 較重要的類是CPreferences和CStatistics。前者掌握著程序的大部分配置數(shù)據(jù),后者則進(jìn)行各種統(tǒng)計(jì)。它們的特點(diǎn)都是有很多的成員變 量,而且還是靜態(tài)的,這種方式可以保證它們的唯一性,而且把這些變量統(tǒng)一到一個(gè)類管理。但是實(shí)際上并不需要了解每個(gè)變量的含義。thePrefs和 theStats是這兩個(gè)類的唯一的實(shí)例。

            在處理完其它一些事情,包括創(chuàng)建圖形界面對(duì)象CemuleDlg后,接下來可以看到一排一排的創(chuàng)建新對(duì)象的語(yǔ)句。這些類將會(huì)實(shí)現(xiàn)eMule程序運(yùn)行的主要功能,后面的系列將詳細(xì)分析。

            最 后描述一下如何在VS2003里編譯eMule,由于eMule中用到了一些其它的庫(kù),因此官方在提供eMule的源代碼下載的同時(shí)如果也提供這些庫(kù)的下 載會(huì)使源碼包變得很大。因此eMule選擇了讓開發(fā)者去那些庫(kù)的官方網(wǎng)站下載它們的方式。一般來說,編譯這種工程文件,很重要的地方是要保持各個(gè)庫(kù)和主程 序編譯參數(shù)的一致性。這些編譯參數(shù)中,最主要的參數(shù)有三個(gè),字符集(多字節(jié)/Unicode),調(diào)試/發(fā)行,單線程/多線程,這樣排列組合一下就有八個(gè)版 本了。因此編譯的時(shí)候如果不注意,就會(huì)出現(xiàn)和程序設(shè)計(jì)無關(guān)的錯(cuò)誤。

            eMule0.47a解壓后自帶id3lib庫(kù),還需要下載以下的庫(kù):

            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

            下 載它們,解壓它們,編譯它們。編譯的時(shí)候注意要和eMule的工程的參數(shù)一致:字符集為Unicode,支持多線程安全,調(diào)試版或者是發(fā)行版可以根據(jù)需要 選擇,但是也要和eMule的工程參數(shù)一致。這些庫(kù)的解壓包里通常都能找到VC的工程文件,但是版本低一些,直接轉(zhuǎn)化就可以了。另外建議編譯這些庫(kù)的時(shí)候 都選擇生成靜態(tài)庫(kù),不要生成動(dòng)態(tài)的庫(kù),這樣最后生成的可執(zhí)行文件就可以自己運(yùn)行了。編譯完這些庫(kù),包括從其它地方下載的和eMule自帶的id3lib庫(kù) 和CxImage庫(kù)后,就可以開始編譯emule了。而編譯emule也無非就是注意讓它能夠在編譯的時(shí)候找到所有的頭文件,以及在鏈接的時(shí)候能夠找到所 有的庫(kù)。在鏈接的時(shí)候能夠找到所有的庫(kù)可以通過修改工程文件里面的屬性 ->配置屬性 ->鏈接器 ->輸入->附加依賴項(xiàng)來完成。但是能夠找到所有的頭文件反而需要一些技巧了。由于emule的代碼中對(duì)于這些庫(kù)的頭文件的包含,在一定程度 上限定了那些庫(kù)的路徑和emule的工程的路徑的相對(duì)位置,因此需要把那些解壓過的庫(kù)的目錄移到一些合適的地方,有時(shí)還需要給這些目錄改個(gè)名稱。

            3. eMule源代碼學(xué)習(xí)心得(3):emule中最重要的幾個(gè)基礎(chǔ)設(shè)施
            eMule 中要讀取的配置文件數(shù)量較多,每種配置文件都是自己定義的格式,為了方便讀取和存儲(chǔ)這些文件,eMule中有一個(gè)很重要的基礎(chǔ)設(shè)施類來復(fù)制這些文件操作, 它能夠很方便得處理一些常用數(shù)據(jù)類型的讀寫,并且?guī)в幸欢ǖ陌踩Wo(hù)機(jī)制。這項(xiàng)基礎(chǔ)設(shè)施在SafeFile.cpp和SafeFile.h中實(shí)現(xiàn)。在 kademlia\io目錄下有這項(xiàng)功能的另外一項(xiàng)實(shí)現(xiàn)。它們實(shí)現(xiàn)的功能基本上相似,但是kademlia\io目錄下的版本實(shí)現(xiàn)的時(shí)候多了一個(gè)以Tag 作為單位進(jìn)行讀寫的功能。這些實(shí)現(xiàn)中和另外一項(xiàng)基礎(chǔ)設(shè)施,那就是字符串轉(zhuǎn)化密切相關(guān)。StringConversion.cpp和 StringConversion.h是eMule中專門復(fù)制各類字符串轉(zhuǎn)化的基礎(chǔ)設(shè)施,什么Unicode啊,多字節(jié)流啊,或者是UTF-8之類的,在 這里轉(zhuǎn)化全部都不是問題。關(guān)于字符串轉(zhuǎn)化,個(gè)人推薦盡量使用Unicode的寬字符,這樣可以最大程度得避免亂碼。

            SafeFile.cpp 或者kademlia\io目錄下的實(shí)現(xiàn)都有這樣的特點(diǎn),那就是把數(shù)據(jù)操作的行為和數(shù)據(jù)操作的對(duì)象分割開來。它們都定義了一個(gè)抽象的數(shù)據(jù)操作的基類(在 SafeFile.cpp中是CFileDataIO,在kademlia目錄下是DataIO.cpp實(shí)現(xiàn)的Kademlia::CDataIO),這 個(gè)類中只負(fù)責(zé)實(shí)現(xiàn)在邏輯上操作一項(xiàng)數(shù)據(jù)的行為,例如,要讀取出一個(gè)32位的整型,那么就是讀出四個(gè)字節(jié)到一個(gè)整型數(shù)值的地址中,要讀取或者寫入其它類型的 數(shù)據(jù)用的是類似的方法。但是這個(gè)類把物理上進(jìn)行數(shù)據(jù)操作的方法全部都聲明為純虛函數(shù),即讀出多少個(gè)字節(jié),寫入多少個(gè)字節(jié)這樣的。有了這樣一個(gè)基類,就可以 非常方便得在它上面進(jìn)行重載,把這些純虛函數(shù)定義為向某塊內(nèi)存中進(jìn)行讀寫的操作,就能很方便得將較為復(fù)雜的數(shù)據(jù)序列化到一塊連續(xù)的內(nèi)存,而如果這些純虛函 數(shù)是向文件讀寫的操作,那么自然就可以很方便得用來讀寫各種格式比較奇怪的自己定義的配置文件了。

            這些類要讀取的數(shù)據(jù)對(duì)象通常有這些,各種 整型,字符串,以及Tag類型。整型讀寫起來比較簡(jiǎn)單,從1個(gè)字節(jié)的,2個(gè)字節(jié)的到4個(gè),8個(gè)或者16個(gè)字節(jié)類型的數(shù)據(jù)讀寫方法都比較類似。這里要稍微提 一下16個(gè)字節(jié)的那種,16個(gè)字節(jié)是128位,是eMule中的Kad網(wǎng)的隨機(jī)生成的ID的長(zhǎng)度,也是eMule中常用的MD4的hash算法生成的結(jié)果 的長(zhǎng)度。通常用來直接存取整個(gè)的這樣的一個(gè)ID。在kademlia\utils目錄下的UInt128.cpp實(shí)現(xiàn)了一個(gè)表示128位的整數(shù)的類,功能 十分完善,可以進(jìn)行一些算術(shù)操作,并且可以進(jìn)行比較,這樣為它以后作為key出現(xiàn)在hash表中打下了基礎(chǔ)。仔細(xì)學(xué)習(xí)UInt128.cpp中的代碼實(shí)現(xiàn) 可以學(xué)到很多在編寫這種自定義的數(shù)據(jù)對(duì)象類型時(shí)應(yīng)該注意的問題。

            數(shù)據(jù)操作中另外一項(xiàng)很重要的操作是字符串,總的原則是先寫一個(gè)長(zhǎng)度,再寫內(nèi) 容。但是到具體的操作的時(shí)候就需要注意這些細(xì)節(jié)了,如長(zhǎng)度是寫4個(gè)字節(jié)還是兩個(gè)字節(jié),字符串的內(nèi)容要不要用UTF-8進(jìn)行編碼。這些操作就需要和 StringConversion.cpp緊密合作了。其實(shí)后者的字符串轉(zhuǎn)化函數(shù)很多也是調(diào)用ATL的相關(guān)函數(shù),只是在外面再包上一層MFC的 CString。

            在kademlia\io\DataIO.cpp中實(shí)現(xiàn)的CDataIO中,還另外實(shí)現(xiàn)了按照Tag進(jìn)行讀寫的功能。這在 網(wǎng)絡(luò)上交換共享文件的元信息非常重要,通常一個(gè)文件的元信息就可以分解成很多的Tag,如"文件名=xxx","文件長(zhǎng)度=xxx"等等。也就是說,一個(gè) Tag就是表示某項(xiàng)屬性等于某個(gè)值這樣一個(gè)事實(shí)。在Opcodes.h這個(gè)文件中定義了很多的代碼,其中就有很多常見的Tag的屬性名稱。CDataIO 類中存儲(chǔ)Tag的屬性名都是先存一個(gè)字節(jié)的類型,再存名稱,最后按照類型存值。

            eMule中的這幾項(xiàng)基礎(chǔ)設(shè)施都是編寫得比較好的,可以很方 便得拿出來復(fù)用。像字符串編碼的處理和具有一定數(shù)據(jù)結(jié)構(gòu)的文件IO操作在很多地方都會(huì)很有用。eMule中這些類的實(shí)現(xiàn)基本上復(fù)制到其它的工程文件中只要 稍微修改一下很快就能使用。以后我們還將看到eMule中很多其它很有用的基礎(chǔ)設(shè)施。 

            4. eMule源代碼學(xué)習(xí)心得(4):對(duì)自己的資源要了如指掌,CKnownFileList類的作用
            emule作為一個(gè)文件共享方面的程序,首先要對(duì)自己共享的所有的文件的信息都十分清楚,類CKnownFileList的作用就是這樣的,它在emule.cpp中隨著cmuleapp類創(chuàng)建的時(shí)候被創(chuàng)建。

            CKnownFileList 類使用了MFC的CMap類來維護(hù)內(nèi)部的hash表,這也可以看出emule和MFC的關(guān)系確實(shí)非常緊密。這里如果用STL的map其實(shí)也是可以的。它內(nèi) 部維護(hù)了一個(gè)已知的文件的列表和取消了的文件列表。這些hash表的關(guān)鍵字都是文件的hash值。這樣能夠判斷出文件名不同而內(nèi)容相同的文件,而一般要讓 不同內(nèi)容的文件有相同的hash值是非常困難的,這也是hash函數(shù)它設(shè)計(jì)的初衷。因此除非是碰上王小云教授這樣的牛人,我們基本上可以認(rèn)為,兩個(gè)文件 hash值相同就代表了它們內(nèi)容相同。再來看CKnownFileList.cpp,這個(gè)文件其實(shí)并不長(zhǎng),因?yàn)楣芾硪粋€(gè)列表確實(shí)不需要太多種類的操作,如 果對(duì)于每個(gè)具體的文件有一個(gè)很強(qiáng)大的類來處理它的話。而這里確實(shí)有,它就是CKnownFile。有了這么一個(gè)類,我們就可以看到, CKnownFileList類所需要做的工作就是能夠根據(jù)一些信息查找到對(duì)應(yīng)的CKnownFile類,能夠復(fù)制其它的列表中的信息,能夠把所有的這些 信息存成文件,然后下次emule運(yùn)行的時(shí)候能夠把這些信息快速恢復(fù)出來,最重要的是能夠在完成以上工作的情況下不造成內(nèi)存泄漏。

            CKnownFile 類就是一個(gè)專門關(guān)注某個(gè)特定文件的信息的類,它仍然有其基類CAbstractFile。但是它和CAbstractFile類的主要區(qū)別就是 CAbstractFile類只有基本的信息存取的功能,而CKnownFile能夠主動(dòng)的生成這些信息,例如,給一個(gè)文件的路徑給 CKnownFile,它能夠主動(dòng)地去獲取和這個(gè)文件有關(guān)的一切信息,并且把它保存在自己的成員變量里(CreateFromFile)。 CKnownFile.cpp文件看上去比較長(zhǎng),是因?yàn)樗龅墓ぷ鞅容^多,現(xiàn)在版本的emule中,除了對(duì)某個(gè)文件進(jìn)行全文hash以外,還采用了BT的 方式,進(jìn)行分塊hash,這樣在傳輸文件的時(shí)候,即使發(fā)生出錯(cuò)的情況,也可以不必重傳整個(gè)文件,而只是重傳有錯(cuò)誤的那塊,這種機(jī)制叫做高級(jí)智能損壞處理 (AICH,Advanced Intelligent Corruption Handling),這個(gè)機(jī)制以后再繼續(xù)分析。

            CKnownFile 把讀到的文件信息都保存成一個(gè)一個(gè)的Tag。它在運(yùn)行中會(huì)盡量得獲取更多的文件信息,例如,對(duì)于媒體類型的文件,它能夠調(diào)用id3lib庫(kù)來獲取諸如作 者,唱片發(fā)行年代,風(fēng)格等tag信息。如果是視頻媒體文件,它還會(huì)去抓圖(功能實(shí)現(xiàn):CFrameGrabThread)。

            CKnownFile 還能夠隨時(shí)掌握目前該文件的下載情況(內(nèi)部有個(gè)CUpDownClient的列表),當(dāng)然,還會(huì)根據(jù)要求序列化和反序列化自己,LoadFromFile 和WriteToFile都以CFileDataIO為參數(shù),這樣方便CKnownFileList保存和讀取它的列表中的所有文件的信息。 

            5. eMule源代碼學(xué)習(xí)心得(5):分塊機(jī)制--正確傳輸資源的保證
            為 了加快內(nèi)容分發(fā)的速度,分塊處理是一種簡(jiǎn)單有效的方法。emule中對(duì)每個(gè)文件都進(jìn)行了分塊處理。另外分塊還有一個(gè)好處就是如果保留了每一分塊的hash 值,就能在只下載到文件的一部分時(shí)判斷出下載內(nèi)容的有效性。emule在獲取每個(gè)共享文件的信息時(shí),就對(duì)它進(jìn)行了分塊處理,因此如果要知道emule中的 分塊處理和恢復(fù)機(jī)制,看CKnownFile::CreateFromFile函數(shù)的實(shí)現(xiàn)就行了。

            這個(gè)函數(shù)中牽涉到的和分塊處理以及hash計(jì)算相關(guān)的類都在SHAHashSet.cpp和SHAHashSet.h中。下面介紹其中幾個(gè)主要的類:

            CAICHHash 類只負(fù)責(zé)一塊hash值,提供兩個(gè)CAICHHash類之間的直接賦值,比較等基本操作。CAICHHashAlgo是一個(gè)hash算法的通用的接口,其 它hash算法只要實(shí)現(xiàn)這種接口都能使用,這樣,可以很方便得使用不同的hash算法來計(jì)算hash值。CAICHHashTree則是一個(gè)樹狀的 hash值組織方式,它有一個(gè)左子樹和右子樹成員變量,類型是指向CAICHHashTree的指針,這是一個(gè)典型的實(shí)現(xiàn)樹狀結(jié)構(gòu)的方法。 CAICHHashSet中包含了一個(gè)CAICHHashTree類型的變量,它直接向CKnownFile負(fù)責(zé),代表的是一個(gè)文件的分塊信息。

            SHAHashSet.h 文件的開始的注釋部分向我們解釋了它的分塊的方式。這里要用到兩個(gè)常量9728000和184320,它們分別是9500k和180k。這是emule中 兩種不同粒度的分塊方式,即首先把一個(gè)很大的文件分割成若干個(gè)9500k的塊,把這些塊組織成一顆樹狀的結(jié)構(gòu),然后每一個(gè)這樣的塊又分解成若干個(gè)180k 的塊(52塊,再加一個(gè)140k的塊),仍然按照樹狀的結(jié)構(gòu)組織起來。最后總的結(jié)構(gòu)還是一顆樹。

            CKnownFile::CreateFromFile 方法是在讀取目標(biāo)文件的內(nèi)容時(shí),逐步建立起這樣一顆樹的。CAICHHashTree::FindHash能夠根據(jù)讀取到的目標(biāo)文件的偏移量和下一塊的大 小,來找出對(duì)應(yīng)的樹枝節(jié)點(diǎn)(就是一個(gè)指向CAICHHashTree的指針)。如果有必要的話,還會(huì)自動(dòng)創(chuàng)建這些樹枝節(jié)點(diǎn)。因此在進(jìn)行分塊操作的時(shí)候,把 文件從頭到尾讀一邊,整個(gè)CAICHHashTree就建立起來了,對(duì)應(yīng)的分塊hash值也賦值好了。最后我們還需要注意的就是CKnownFile類中 的hashlist變量。就是說它還單獨(dú)保留直接以9728000字節(jié)為單位的所有分塊的MD4算法的hash值。這樣對(duì)于一個(gè)文件就有了兩套分塊驗(yàn)證的 機(jī)制,能夠適應(yīng)不同場(chǎng)合。

            6. eMule源代碼學(xué)習(xí)心得(6):網(wǎng)絡(luò)基礎(chǔ)設(shè)施--網(wǎng)絡(luò)基礎(chǔ)設(shè)施的基礎(chǔ)設(shè)施
            MFC中已經(jīng)有一些網(wǎng)絡(luò)基礎(chǔ)設(shè)施類,如CAsyncSocket等。但是emule在設(shè)計(jì)中,為了能夠更加高效得開發(fā)網(wǎng)絡(luò)相關(guān)的代碼,構(gòu)建了另外的一些類作為基礎(chǔ)設(shè)施,這些基礎(chǔ)設(shè)施類的代碼也有很高的復(fù)用價(jià)值。

            首 先是CAsyncSocketEx類。AsyncSocketEx.h中對(duì)這個(gè)類的特點(diǎn)已經(jīng)給出了一定的說明。它完全兼容CAsyncSocket類,即 把應(yīng)用程序中所以的CAsyncSocket換成CAsyncSocketEx,程序仍然能夠和原來的功能相同,因此在使用上更加方便。但是在這個(gè)基礎(chǔ) 上,它的效率更高,主要是在消息分發(fā)機(jī)制上,即它處理和SOCKET相關(guān)的消息的效率要比原始的MFC的CAsyncSocket類更高。另外, CAsyncSocketEx類支持通過實(shí)現(xiàn)CAsyncSocketExLayer類的方式,將一個(gè)SOCKET分成若干個(gè)層,從而可以很方便得實(shí)現(xiàn)許 多網(wǎng)絡(luò)功能,如設(shè)置代理,或者是使用SSL進(jìn)行加密等。

            另外還有ThrottledSocket.h中定義的 ThrottledControlSocket類和ThrottledFileSocket類,這兩個(gè)類只定義了兩個(gè)接口。任何其它的網(wǎng)絡(luò)套接字類如果想 實(shí)現(xiàn)限速的功能,只需要在其默認(rèn)的發(fā)送函數(shù)(如Send或Sendto)中不發(fā)送數(shù)據(jù)而是把數(shù)據(jù)緩存起來,然后在實(shí)現(xiàn) ThrottledControlSocket或者ThrottledFileSocket接口中的SendFileAndControlData或 SendControlData方法時(shí)才真正把數(shù)據(jù)發(fā)送出去,這樣就能實(shí)現(xiàn)上傳限速,而這也是需要UploadBandwidthThrottler類進(jìn) 行配合,UploadBandwidthThrottler是一個(gè)WinThread的子類,平時(shí)單獨(dú)運(yùn)行一個(gè)線程。下一次會(huì)詳細(xì)描述它是如何控制全局的 上傳速度的。 

            7. eMule源代碼學(xué)習(xí)心得(7):網(wǎng)絡(luò)基礎(chǔ)設(shè)施--全局限速器UploadBandwidthThrottler
            UploadBandwidthThrottler 是emule中使用的全局的上傳限速器。它繼承了CWinThread類,且在該類被創(chuàng)建的時(shí)候,就新創(chuàng)建一個(gè)線程開始單獨(dú)運(yùn)行。在該類被析構(gòu)時(shí)也會(huì)自動(dòng) 停止相應(yīng)的線程。這個(gè)線程的目標(biāo)函數(shù)就是RunProc,然后為了避免在RunProc函數(shù)不能使用this指針的情況,它使用了RunInternal 來實(shí)際完成工作線程的工作。在emule中,還有另外一個(gè)類LastCommonRouteFinder有類似的結(jié)構(gòu)。

            UploadBandwidthThrottler 中保存了若干的套接字(Socket)隊(duì)列,這些隊(duì)列的處理方式略有不同。在標(biāo)準(zhǔn)隊(duì)列(m_StandardOrder_list)里面排隊(duì)的都是實(shí)現(xiàn)了 ThrottledFileSocket接口的類,通常這些類能夠傳輸文件內(nèi)容也可以傳輸控制信息。而其它四個(gè)隊(duì)列都是實(shí)現(xiàn) ThrottledControlSocket接口的類的隊(duì)列,在這些隊(duì)列中的類主要以傳輸控制信息為主。這四個(gè)隊(duì)列為臨時(shí)高優(yōu)先級(jí),臨時(shí)普通優(yōu)先級(jí),正 式高優(yōu)先級(jí),正式普通優(yōu)先級(jí)。和把套件字直接添加到普通隊(duì)列(AddToStandardList)不同, QueueForSendingControlPacket把要添加到隊(duì)列的套接字全部添加到兩個(gè)臨時(shí)隊(duì)列。根據(jù)它們的優(yōu)先級(jí)添加到普通的臨時(shí)隊(duì)列。在 RunInternal的大循環(huán)中,臨時(shí)隊(duì)列中的項(xiàng)目先被移到普通隊(duì)列中,然后再進(jìn)行處理。

            UploadBandwidthThrottler 使用了兩個(gè)臨界區(qū),兩個(gè)事件。pauseEvent是用來暫停整個(gè)大循環(huán)的動(dòng)作的。而threadEndedEvent是標(biāo)志整個(gè)線程停止的事件。 sendLocker是大循環(huán)中使用的主要的臨界區(qū),而tempQueueLocker是為兩個(gè)臨時(shí)隊(duì)列額外添加的鎖,這樣可以一邊發(fā)送已有隊(duì)列中的套界 字要發(fā)送的數(shù)據(jù),一邊把新的套接字加到隊(duì)列中。

            UploadBandwidthThrottler的RunInternal中的大循環(huán)是該 工作線程的日常操作。這個(gè)大循環(huán)中做了以下事情,計(jì)算本次配額,即本次循環(huán)中能夠發(fā)送多少字節(jié),好安排調(diào)度,計(jì)算本次循環(huán)應(yīng)該睡眠多少時(shí)間,然后進(jìn)行相應(yīng) 的睡眠,從而進(jìn)行限速。操作控制信息隊(duì)列,發(fā)送該隊(duì)列中的數(shù)據(jù),注意,控制隊(duì)列中的套接字(m_ControlQueueFirst_list和 m_ControlQueue_list)只使用一次就離開隊(duì)列。而標(biāo)準(zhǔn)隊(duì)列中的套接字不會(huì)這樣。在一輪循環(huán)結(jié)束后,如果還有沒有用完的發(fā)送數(shù)據(jù)的配額, 則會(huì)有部分配額保存到下一輪。

            8. eMule源代碼學(xué)習(xí)心得(8):網(wǎng)絡(luò)基礎(chǔ)設(shè)施--emule套接字CEMSocket
            CEMSocket 是CAsyncSocketEx和ThrottledFileSocket的子類,它把若干功能整合到了一起,因此可以作為emule使用起來比較方便的 套接字。例如它可以很方便得指定代理,把CAsyncSocketEx中的創(chuàng)建一個(gè)新的代理層并且添加到列表中的功能對(duì)外屏蔽了。另外它可以分出狀態(tài),如 當(dāng)前是否在發(fā)送控制信息等。

            CEMSocket中我們需要仔細(xì)考察的是它的SendControlData和 SendFileAndControlData方法。如前所述,這些方法是用來和UploadBandwidthThrottler進(jìn)行配合,以便完成全 局的限速功能的。它的功能應(yīng)該是按照UploadBandwidthThrottler的要求,在本次輪到它發(fā)送數(shù)據(jù)時(shí)發(fā)送指定數(shù)量的字節(jié)數(shù)。因此,應(yīng)用 程序的其它部分在使用CEMSocket時(shí),如果要達(dá)到上傳數(shù)據(jù)限速的目的,不應(yīng)該直接調(diào)用標(biāo)準(zhǔn)的Send或者SendTo方法,而是調(diào)用 SendPacket。這里就有了另外一個(gè)結(jié)構(gòu)Packet,它通常包含一個(gè)emule協(xié)議中完整的包,例如有協(xié)議的頭部數(shù)據(jù)等,還內(nèi)置了 PackPacket和UnPackPacket方法,可以自行進(jìn)行壓縮和解壓的功能。SendPacket把要發(fā)送的Packet放到自己的隊(duì)列中,這 個(gè)隊(duì)列也有兩個(gè),控制信息包隊(duì)列,和標(biāo)準(zhǔn)信息包隊(duì)列。如果有必要,把自己加入到UploadBandwidthThrottler的隊(duì)列中。

            我 們注意到CEMSocket的SendControlData和SendFileAndControlData方法其實(shí)都是調(diào)用自己的另一個(gè)重載的 Send方法。而且我們也已經(jīng)知道這個(gè)方法是在UploadBandwidthThrottler的工作線程中的大循環(huán)中被調(diào)用的,而這個(gè)Send方法的 內(nèi)容本身也是一個(gè)大循環(huán),但是意義很明了,就是在不超過自己本次發(fā)送的配額的情況下,把自己的包隊(duì)列中的包取出來,并且發(fā)出去。同樣,這里也用到了一個(gè)臨 界區(qū),它是為了保證從包隊(duì)列中取出包來發(fā)送和把包往隊(duì)列中放的操作是互斥的。因此,如果把它和UploadBandwidthThrottler結(jié)合起 來,我們就看到了一個(gè)兩層的隊(duì)列,即所有的套接字組成了一個(gè)發(fā)送隊(duì)列,在UploadBandwidthThrottler的控制下保證了對(duì)速度的限制, 而每個(gè)套接字即將發(fā)送的數(shù)據(jù)包又組成了一個(gè)隊(duì)列,保證了每次進(jìn)行數(shù)據(jù)發(fā)送的時(shí)候都會(huì)滿足UploadBandwidthThrottler的要求。 

            9. eMule源代碼學(xué)習(xí)心得(9):搜索信息集-CSearchList
            CSearchList 是emule中的搜索列表,掌管emule中所有的搜索請(qǐng)求。CSearchFile是這個(gè)列表中的元素,代表了一次搜索的相關(guān)信息。它們的關(guān)系和之前描 述的已知文件和已知文件列表有一些類似的地方。CSearchList的主要任務(wù)就是對(duì)其一個(gè)叫做list的類型為CSearchFile列表的內(nèi)部變量 進(jìn)行維護(hù),提供很方便得往這個(gè)列表中添加,刪除,查詢,變更等操作的接口。另外,每一個(gè)搜索都有一個(gè)ID,是一個(gè)32位的整數(shù)。CSearchList中 記錄了每個(gè)搜索目前搜到的文件個(gè)數(shù)和源的個(gè)數(shù)(m_foundFilesCount和m_foundSourcesCount)。

            CSearchFile 是CAbstractFile的另一個(gè)子類(CKnownFile也是),它保存了某個(gè)文件和搜索相關(guān)的信息,而不是這個(gè)文件本身的信息(這些信息在 CAbstractFile中已經(jīng)包括了),這些和搜索有關(guān)的信息就是都在哪些機(jī)器上有這個(gè)文件,以及哪個(gè)服務(wù)器上搜到的這個(gè)文件。甚至還可以向搜索文件 添加預(yù)覽。在這個(gè)類的定義中嵌套定義了兩個(gè)簡(jiǎn)單的結(jié)構(gòu)SServer和SClient,表示了該搜索文件的可能來源,服務(wù)器或者其它客戶端。 m_aClients和m_aServers是這兩個(gè)簡(jiǎn)單結(jié)構(gòu)的一個(gè)數(shù)組,CSearchFile自然也提供了對(duì)這個(gè)數(shù)組的操作的接口,方便 CSearchList使用。

            CSearchList對(duì)外提供了搜索表達(dá)的接口,即每當(dāng)有一個(gè)新的搜索提交時(shí)CSearchList:: NewSearch會(huì)建立一個(gè)新的搜索項(xiàng),但是此時(shí)還沒有任何對(duì)應(yīng)的搜索文件,因此只是在文件個(gè)數(shù)和搜索ID的對(duì)應(yīng)表 (m_foundFilesCount和m_foundSourcesCount)中建立新的項(xiàng)目。另外當(dāng)有搜索結(jié)果返回時(shí) ProcessSearchAnswer或ProcessUDPSearchAnswer能夠?qū)Ψ祷氐陌苯幼鎏幚恚瑒?chuàng)建相應(yīng)的搜索文件信息 CSearchFile對(duì)象,并加入到自己的列表中。當(dāng)然,要把重復(fù)的搜索結(jié)果去除,發(fā)現(xiàn)同一個(gè)hash的文件的多個(gè)源時(shí)也會(huì)給它們建立一個(gè)二級(jí)列表 (CSearchFile::m_list_parent)。現(xiàn)在我們可以看出,CSearchList只負(fù)責(zé)和搜索有關(guān)的信息的儲(chǔ)存和讀取,本身并不進(jìn) 行搜索。 

            10. eMule源代碼學(xué)習(xí)心得(10):服務(wù)器信息集-CServerList
            嗯,明天要回北京了,今天打算好好休息一下,故挑了個(gè)內(nèi)容少的來說,呵呵。

            盡 管目前有了Kad網(wǎng)絡(luò),但是使用服務(wù)器來獲取各個(gè)emule用戶的共享文件列表仍然是emule中主要的資源獲取方式。CServerList就是 emule中負(fù)責(zé)管理服務(wù)器列表的類。和前面若干列表類結(jié)構(gòu)類似,CServerList需要對(duì)外提供列表的增加,刪除,查找,修改等接口。在 CServerList中,每個(gè)服務(wù)器的信息是一個(gè)CServer類。和搜索信息不一樣,但是和已知文件列表一樣,服務(wù)器的信息列表是需要長(zhǎng)期保留的,因 此CServerList和CKnownFileList類一樣提供了把它所包含的所有信息保存到一個(gè)文件中,以及從這個(gè)文件中讀回其信息的功能。

            CServer 中的結(jié)構(gòu)比較簡(jiǎn)單,只需要保留服務(wù)器的各種信息即可。它可以通過IP地址和端口來創(chuàng)建,也可以通過一個(gè)簡(jiǎn)單的結(jié)構(gòu)ServerMet_Struct來創(chuàng) 建,其中后者是用來直接從文件中讀取的。該結(jié)構(gòu)僅僅包含IP地址和端口以及屬性的個(gè)數(shù),CServer中其它的屬性在保存到文件中時(shí),均采用Tag方式保 存。

            CServerList除了提供通常的CServer信息外,還提供一些統(tǒng)計(jì)信息諸如所有的服務(wù)器的用戶數(shù),共享的文件數(shù)等。這些統(tǒng)計(jì)信息也是基于每個(gè)單獨(dú)的CServer的相關(guān)信息計(jì)算出來的。


            11. eMule源代碼學(xué)習(xí)心得(11):emule的通信協(xié)議-一些基本的約定
            接 下來將不可避免得要碰到emule的協(xié)議。emule的通信協(xié)議格式設(shè)計(jì)成一種便于擴(kuò)充的格式。對(duì)于TCP連接來說,連接中的數(shù)據(jù)流都能夠劃分成為一個(gè)一 個(gè)的Packet,CEMSocket類中就完成了把接收到的數(shù)據(jù)劃分成Packet這一工作。但是具體的對(duì)于每個(gè)Packet進(jìn)行處理的工作被轉(zhuǎn)移到它 的子類中進(jìn)行。CEMSocket類的兩個(gè)子類CServerSocket和CClientReqSocket所代表的TCP連接就分別是客戶端和服務(wù)器 之間的TCP連接以及客戶端之間的TCP連接。在數(shù)據(jù)流中的第一個(gè)字節(jié)代表的是通信的協(xié)議簇代碼,如0xE3為標(biāo)準(zhǔn)的edonkey協(xié)議,0xE4為 kademlia協(xié)議等等。接下來的四個(gè)字節(jié)代表包內(nèi)容的長(zhǎng)度,所有的包都用這種方式發(fā)送到TCP流中,就可以區(qū)分出來了。另外每個(gè)包內(nèi)容中的第一個(gè)字節(jié) 為opcode,即在確定了某個(gè)具體協(xié)議后,這個(gè)opcode確定了這個(gè)包的具體含義。


            對(duì)于走UDP協(xié)議的包,處理起來更加得簡(jiǎn) 單,因?yàn)閁DP本來就是以一個(gè)包一個(gè)包作為單位在網(wǎng)絡(luò)上流傳的,因此不需要在包的內(nèi)容中再包含表示長(zhǎng)度的字段。每個(gè)UDP包的第一個(gè)字節(jié)是協(xié)議簇代碼,其 它內(nèi)容就是包的內(nèi)容。CClientUDPSocket類負(fù)責(zé)處理客戶端和客戶端之間的UDP包,而CUDPSocket類負(fù)責(zé)處理客戶端和服務(wù)器之間的 UDP包。另外還有個(gè)Kademlia::CKademliaUDPListener類,專門處理和Kademlia協(xié)議相關(guān)的UDP包。

            最 后說一下Packet類,這個(gè)類以前只是提到過。它是emule的通信協(xié)議的最小單位。我們可以看出,它的構(gòu)造函數(shù)有多個(gè)版本,這也是為了可以用不同的方 式來創(chuàng)建Packet。例如只包含一個(gè)頭部信息的緩沖區(qū),或者只是指定協(xié)議簇代碼等。而且它內(nèi)部實(shí)現(xiàn)了壓縮和解壓的方法,該方法直接調(diào)用zlib庫(kù)中的壓 縮方法,可以減少數(shù)據(jù)的傳輸量。這里要注意一點(diǎn)的就是壓縮的時(shí)候協(xié)議簇代碼是不參與壓縮的,壓縮完畢后會(huì)更換協(xié)議簇代碼,例如代碼為標(biāo)準(zhǔn)edonkey協(xié) 議0xE3的包在壓縮后,協(xié)議代碼就變成0xD4了,這里進(jìn)行協(xié)議代碼變化是為了使接受方能夠正確識(shí)別并且進(jìn)行相應(yīng)的解壓操作。

            12. eMule源代碼學(xué)習(xí)心得(12):emule的通信協(xié)議-客戶端和服務(wù)器之間的通信概述
            客 戶端和服務(wù)器之間的所有通信由類CServerConnect掌握。CServerConnect本身不是套接字的子類,但是它的成員變量 CServerSocket類型的connectedsocket是。CServerConnect內(nèi)部有一列表,可以保存若干 CServerSocket類型的指針。但是這并不說明它平時(shí)連接到很多服務(wù)器上。它只是可以同時(shí)試圖連接到若干個(gè)服務(wù)器上,這只是因?yàn)檫B接到服務(wù)器上的 行為不一定能成功。

            CServerSocket類是CEMSocket的子類,它比CEMSocket要多保存一些狀態(tài),比如當(dāng)前的服務(wù)器 連接狀態(tài)。它同時(shí)還保留它當(dāng)前所連接的服務(wù)器的信息。通過分析CServerSocket::ProcessPacket就可以直接把emule客戶端和 服務(wù)器之間的通信協(xié)議理解清楚,這里是服務(wù)器發(fā)回的包。TCP連接建立后的第一個(gè)包是在CServerConnect:: ConnectionEstablished中發(fā)出的,即向服務(wù)器發(fā)出登陸信息。如果登陸成功,則能夠從服務(wù)器處獲取自己的ID,這是一個(gè)32位的長(zhǎng)整 數(shù)。如果這個(gè)數(shù)小于16777216,那么我們稱它為L(zhǎng)owID。具有LowID的客戶端通常情況下其它客戶端將不能直接連接它。得到LowID的原因比 較多,例如當(dāng)自己處于NAT的后端的時(shí)候。獲取自己的ID后將會(huì)向服務(wù)器發(fā)送自己的共享文件列表,這一動(dòng)作由共享文件列表類 CSharedFileList來完成。

            其它類型包沒有必要全部都列出來,以后可以通過在分析其它部分時(shí),因?yàn)闋可娴酵?wù)器發(fā)送或者接受數(shù)據(jù)的時(shí)候再進(jìn)行相應(yīng)的分析。 

            13. eMule源代碼學(xué)習(xí)心得(13):emule的通信協(xié)議-客戶端和客戶端之間的通信概述
            客 戶端和客戶端之間的TCP通信由CListenSocket和CClientReqSocket完成。這也是提供網(wǎng)絡(luò)服務(wù)的應(yīng)用程序的典型寫法。其中 CListenSocket只是CAsyncSocketEx的子類,只負(fù)責(zé)監(jiān)聽某個(gè)TCP端口。它只是內(nèi)部有一個(gè)CClientReqSocket類的 列表。而CClientReqSocket是CEMSocket的子類,因此它能夠自動(dòng)完成emule的packet識(shí)別工作。它有 ProcessPacket和ProcessExtPacket來處理客戶端和客戶端之間的包,其中前者是經(jīng)典的eDonkey協(xié)議的包,后者是 emule擴(kuò)展協(xié)議的包。

            CListenSocket和CClientReqSocket類之間的關(guān)系和前面分析的列表類和它對(duì)應(yīng)的成員類 的關(guān)系是相似的,CListenSocket提供對(duì)自身的CClientReqSocket列表中的元素的增加,查詢,刪除等操作。同時(shí)也維護(hù)關(guān)于這些成 員的一些統(tǒng)計(jì)信息。我們注意到CListenSocket在其構(gòu)造函數(shù)中就把自己添加到CListenSocket類 (theApp.listensocket,該類的唯一實(shí)際示例)的列表中。

            CClientReqSocket類和 CUpDownClient類之間存在著對(duì)應(yīng)關(guān)系。它們都表示了另外一個(gè)客戶端的一些信息,但是CClientReqSocket類主要側(cè)重在網(wǎng)絡(luò)數(shù)據(jù)方 面,即負(fù)責(zé)兩邊的互相通信,而CUpDownClient類負(fù)責(zé)的是從邏輯上對(duì)網(wǎng)絡(luò)另一邊的一個(gè)客戶端進(jìn)行表達(dá)。CUpDownClient類代碼很長(zhǎng), 以后再說。 

            14. eMule源代碼學(xué)習(xí)心得(14):emule中的信譽(yù)機(jī)制
            信譽(yù)機(jī)制在P2P系統(tǒng)中有非常重要的作用。為 了使用戶更加愿意共享自己的資源,需要有一些機(jī)制能夠讓對(duì)整個(gè)P2P系統(tǒng)貢獻(xiàn)更大的用戶有更多的激勵(lì)。在emule中,激勵(lì)機(jī)制的設(shè)計(jì)方案是tit- for-tat這種最直觀的方案。這種方案的意義就是最簡(jiǎn)單的如果別人對(duì)你好,那么你也對(duì)別人好。

            下面看實(shí)際的實(shí)現(xiàn)。 CClientCreditsList和CClientCredits類負(fù)責(zé)emule中的信譽(yù)機(jī)制。我們?cè)俅我姷竭@種列表和元素之間的關(guān)系,不必再重復(fù) 那些語(yǔ)言。和信譽(yù)相關(guān)的信息是需要永久保存的,這樣才有意義,因此CClientCreditsList提供了LoadList和SaveList方法。 我們另外注意到,CClientCredits類可以使用CreditStruct結(jié)構(gòu)來創(chuàng)建,而CreditStruct結(jié)構(gòu)只包含靜態(tài)信息。主要是上 傳量和下載量等。

            信譽(yù)機(jī)制的信息需要有一定的可靠性,在emule中采用了數(shù)字簽名的方式來做到這一點(diǎn)。Crypto++庫(kù)為emule全 程提供和數(shù)字簽名驗(yàn)證相關(guān)的功能。CClientCreditsList在創(chuàng)建時(shí),會(huì)裝載自己的公鑰私鑰,如果沒有的話,會(huì)創(chuàng)建一對(duì)。 CClientCreditsList中包含的有效的信息都是經(jīng)過其它人數(shù)字簽名的,所以更加有信服力。在實(shí)際使用中,這些信息和自己的私鑰要注意保存。 重裝emule后應(yīng)該把配置文件目錄先備份,這樣能夠保留自己辛辛苦苦積攢的信譽(yù)。 

            15. eMule源代碼學(xué)習(xí)心得(15):下載任務(wù)即部分文件的表示
            CPartFile 類是emule中用來表示一個(gè)下載任務(wù)的類。從它的名字也可以看出來,這就是一個(gè)還沒有完成的文件。當(dāng)一個(gè)下載任務(wù)被創(chuàng)建時(shí),emule會(huì)在下載目錄中創(chuàng) 建兩個(gè)文件,以三位數(shù)字加后綴part的文件,例如001.part,002.part等。還有一個(gè)以同樣的數(shù)字加上.part.met的文件,表示的是 對(duì)應(yīng)文件的元信息。part文件會(huì)創(chuàng)建得和原始文件大小一樣,當(dāng)下載完成后,文件名會(huì)修改成它本來的名稱。而事實(shí)上,諸如這個(gè)文件原來叫什么名稱,修改日 期等等信息都在對(duì)應(yīng)的.part.met元文件中。.part.met中還包含了該文件中那些部分已經(jīng)下載完成的信息。

            CPartFile 類中Gap_Struct來表示文件的下載情況,一個(gè)Gap_Struct就是一個(gè)坑,它表示該文件從多少字節(jié)的偏移到多少字節(jié)偏移是一個(gè)坑。下載的過程 就是一個(gè)不斷填坑的過程。CPartFile類中有個(gè)成員變量gaplist就是該文件目前的坑的狀況列表。需要主要的是有時(shí)填了坑的中間部分后,會(huì)把一 個(gè)坑變成兩個(gè)坑。坑的列表也會(huì)被存進(jìn).part.met中。

            CPartFile類的代碼很龐大,但是這是必須的。首先,它的創(chuàng)建就有幾種可 能,從搜索文件CSearchFile中創(chuàng)建,這種情況發(fā)生在用戶搜索到他想要的文件后點(diǎn)擊下載時(shí)發(fā)生。從一個(gè)包含了ed2k鏈接的字符串中創(chuàng)建,它會(huì)提 取出該ed2k鏈接中的信息,并用來創(chuàng)建CPartFile。剩下的一種,就是當(dāng)emule程序重啟后,恢復(fù)以前的下載任務(wù)。這時(shí)就是去下載目錄中尋找那 些.part和.met文件了。另外它還需要不斷得處理下載到的數(shù)據(jù),為了減少磁盤開銷,使用了Requested_Block_Struct結(jié)構(gòu)來暫存 寫入的數(shù)據(jù)。它內(nèi)部維護(hù)一個(gè)CUpDownClient的列表,如果知道了該文件的一個(gè)新的來源信息,就會(huì)創(chuàng)建一個(gè)對(duì)應(yīng)的CUpDownClient。后 者是emule中代碼量最大的類。它還要把它的狀態(tài)用彩色的條裝物顯示出來提供給GUI。

            前面提到的AICH機(jī)制對(duì)于最大程度得保證下載文件的正確性以及盡量減少重復(fù)傳輸都有很大的幫助,在下載的過程中,該機(jī)制會(huì)經(jīng)常對(duì)下載到的數(shù)據(jù)進(jìn)行校驗(yàn)。

            最 后提一下它的Process方法。該方法是emule中為了盡量減少線程的使用而采取的一種有一些類似于輪詢的機(jī)制。其它很多類中也有Process方 法,這個(gè)方法要做的事情就是在一些和日常運(yùn)行有關(guān)的事情,例如檢查為了下載該文件而鏈接到自己的各個(gè)客戶端的狀態(tài),向它們發(fā)送下載請(qǐng)求等。

            16. eMule源代碼學(xué)習(xí)心得(16):下載任務(wù)隊(duì)列
            CDownloadQueue 是下載隊(duì)列類。這個(gè)隊(duì)列中的項(xiàng)目是CPartFile指針。因此和emule中出現(xiàn)的很多其它的列表類一樣,它需要能夠提供對(duì)這個(gè)列表中的元素進(jìn)行增加, 查詢,刪除的功能。例如查詢的時(shí)候能夠根據(jù)該文件的hashID或者索引來進(jìn)行查詢。CDownloadQueue同時(shí)還要完成一些統(tǒng)計(jì)工作。

            和 其它的列表類不一樣的是,它的所有元素的信息并不是集中存放于一個(gè)文件,而是對(duì)應(yīng)于每一個(gè)下載任務(wù),單獨(dú)得存放在一個(gè)元信息文件(.part.met) 中,因此當(dāng)該類進(jìn)行初始化的時(shí)候,它需要尋找所有可能的下載路徑,從那些路徑中找到所有的.part.met文件,并且試圖用這些文件來生成 CPartFile類,并且將這些通過.part.met文件正確生成的CPartFile類添加到自己的列表中,同樣,在退出時(shí),所有的下載任務(wù)的元信 息也是自行保存,不會(huì)合成為一個(gè)文件。

            CDownloadQueue中的Process方法的主要任務(wù)就是把它的列表中的 CPartFile類中的Process方法都調(diào)一遍,另外主要的一些關(guān)于下載情況的統(tǒng)計(jì)信息也是在每一輪的Process后進(jìn)行更新的。從這里我們也可 以看出Process方法在emule中的意義,就是一個(gè)需要經(jīng)常執(zhí)行的方法,通過經(jīng)常執(zhí)行它們來完成日常工作,而且所有的這些Process方法肯定是 順序執(zhí)行,因此可以減少很多多線程的同步之類的問題。emule中已經(jīng)盡量減少了多線程的使用,但是在很多地方如果多線程是不可避免的話,也不會(huì)排斥。

            17. eMule源代碼學(xué)習(xí)心得(17):上傳任務(wù)隊(duì)列
            CUploadQueue是上傳隊(duì)列類。這個(gè)列表類中只有以 CUpDownClient為元素的列表,它和其它列表類還有一個(gè)很大的不同就是它所保存的信息都不需要持久化,即不需要在當(dāng)前的emule退出后還記住 自己正在給誰(shuí)上傳文件,然后下次上線的時(shí)候再繼續(xù)給他們傳,這在大部分情況下是沒有意義的。

            上傳隊(duì)列類列表中有兩個(gè)列表,上傳列表和排隊(duì)列表。當(dāng)一個(gè)收到一個(gè)新的下載請(qǐng)求后,它會(huì)把對(duì)應(yīng)的客戶端先添加到排隊(duì)列表中,以后再根據(jù)情況,把它們不斷添加到上傳列表中。在這里,信譽(yù)機(jī)制將會(huì)對(duì)此產(chǎn)生影響。

            CUploadQueue的Process方法就相對(duì)簡(jiǎn)單了,那就是向上傳隊(duì)列中的所有客戶端依次發(fā)送數(shù)據(jù),而排隊(duì)的客戶端是不會(huì)得到這個(gè)機(jī)會(huì)的。另外它還需要完成關(guān)于上傳方面的一些統(tǒng)計(jì)信息。

            另 外我們還需要注意在CUploadQueue的構(gòu)造函數(shù)里面,創(chuàng)建了一個(gè)以100毫秒為間隔的定時(shí)器,這個(gè)定時(shí)器成為以上所有的Process所需要的基 礎(chǔ)。我們看它的UploadTimer就可以看出這一點(diǎn)。這里面充斥了各個(gè)類的Process方法的執(zhí)行,其中包括以前我們提到的一些類,但是沒有提到它 們的Process方法,因?yàn)槠溥^于簡(jiǎn)單,基本上就只是更新了一下要保存的信息。 

            18. eMule源代碼學(xué)習(xí)心得(18):emule中代碼量最大的類CUpDownClient
            CUpDownClient 類的作用是從邏輯上表示一個(gè)其它的客戶端的各種信息,它是emule中代碼量最大的類。我們注意到,定義它的頭文件是UpDownClient.h,但是 卻沒有對(duì)應(yīng)的CUpDownClient.cpp,而它的實(shí)現(xiàn),都分散到BaseClient.cpp,DownloadClient.cpp, PeerCacheClient.cpp,UploadClient.cpp和URLClient.cpp中。

            BaseClient.cpp 中實(shí)現(xiàn)的是該類的一些基本的功能,包括基本的各種狀態(tài)信息的獲取和設(shè)置,以及按照要求處理和發(fā)送各種請(qǐng)求。在這里,邏輯實(shí)現(xiàn)和網(wǎng)絡(luò)進(jìn)行了區(qū)分, CUpDownClient類本身不從網(wǎng)絡(luò)接受或者發(fā)送消息,它只是提供各種請(qǐng)求的處理接口,以及在發(fā)送請(qǐng)求時(shí),構(gòu)造好相應(yīng)的Packet,并交給自己對(duì) 應(yīng)的網(wǎng)絡(luò)套接字發(fā)出去。

            DownloadClient.cpp中實(shí)現(xiàn)的是和下載相關(guān)的功能,它包括了各種下載請(qǐng)求的發(fā)送以及相應(yīng)的數(shù)據(jù)的接 收。另外還有一個(gè)A4AF的機(jī)制,它是emule中的一個(gè)機(jī)制,因?yàn)橐粋€(gè)客戶端在同一個(gè)時(shí)間內(nèi)只能向另外一個(gè)客戶端請(qǐng)求同一個(gè)文件。這樣,對(duì)于很多個(gè)下載 任務(wù)(CPartFile),有可能出現(xiàn)它們的源(即有該文件的客戶端)有部分重疊的現(xiàn)象,而這時(shí),如果其它下載任務(wù)正在從這個(gè)源下載,那么當(dāng)前的下載任 務(wù)就不能從這個(gè)源下載了。但是emule允許用戶對(duì)其手動(dòng)進(jìn)行控制,如對(duì)下載任務(wù)的優(yōu)先級(jí)進(jìn)行區(qū)分,這樣他就可以將一個(gè)源從另外一個(gè)下載任務(wù)那里切換過 來。A4AF其實(shí)就是ask for another file的簡(jiǎn)稱。

            UploadClient.cpp中實(shí)現(xiàn)的是上傳相關(guān)功能,即接受進(jìn)來的下載請(qǐng)求,并且生成相應(yīng)的文件塊發(fā)送出去。

            PeerCacheClient.cpp 實(shí)現(xiàn)的是和PeerCache相關(guān)的功能,PeerCache是一個(gè)由Joltid公司開發(fā)的技術(shù),它可以允許你從ISP提供的一些快照服務(wù)器上快速得上 傳或者下載一些文件(或者是一部分),這個(gè)技術(shù)的好處是可以減少骨干網(wǎng)絡(luò)的帶寬消耗,將部分本來需要在骨干網(wǎng)上走的流量轉(zhuǎn)移到ISP的內(nèi)部。當(dāng)然這個(gè)功能 需要ISP的配合。如果發(fā)現(xiàn)ISP提供了這項(xiàng)服務(wù)的話,emule會(huì)利用它來減少骨干網(wǎng)的帶寬消耗。

            URLClient.cpp實(shí)現(xiàn)的功能是利用http協(xié)議對(duì)原有的emule協(xié)議進(jìn)行包裝,以便使它能夠盡可能地穿越更多的網(wǎng)絡(luò)的防火墻。 

            19. eMule源代碼學(xué)習(xí)心得(19):emule常規(guī)部分小結(jié)
            emule 中還有其它的很多類,它們使得emule的功能更加的強(qiáng)大和完善。有很多類在前面沒有提到,但是不代表它沒有作用。而且即時(shí)是前面提到的類也只是大體的介 紹,它們之間互相配合的一些細(xì)節(jié)沒有體現(xiàn)。但是這些細(xì)節(jié)應(yīng)該已經(jīng)可以通過對(duì)它們的大體的功能的了解而更加容易被把握。至于GUI的設(shè)計(jì),它也最終是要對(duì)應(yīng) 到某個(gè)功能實(shí)現(xiàn)類的數(shù)據(jù)的。

            對(duì)于emule中的通信協(xié)議只是大體得描述了一下它的數(shù)據(jù)包的格式,但是并沒有詳細(xì)得描述它的每一個(gè) Opcode對(duì)應(yīng)的包的意義,因?yàn)槲艺J(rèn)為這是沒有必要的,在知道通信協(xié)議的格式以及處理它們的代碼所在的位置后,可以很簡(jiǎn)單的通過追蹤某條消息的前因后果 把整個(gè)通信協(xié)議都分析出來。

            這里再稍微提一下在emule中使用到的其它類及其功能。我們可以看到,如果單純只是為了能夠搜到以及下載到文 件的話,有不少類是可以精簡(jiǎn)的,但是,正是由于它們的存在,使得emule的功能更加的完善。CIPFilter,IP地址過濾器,通過識(shí)別各種類型的 IP地址過濾信息,它能夠把不希望連接的網(wǎng)絡(luò)地址過濾掉,emule中所有需要連接網(wǎng)絡(luò)的地方使用的都是統(tǒng)一的過濾數(shù)據(jù)。CWebServer能夠在本地 打開一個(gè)Web服務(wù)器,然后你可以通過瀏覽器來控制你的emule。CScheduler能夠?qū)崿F(xiàn)下載任務(wù)的定時(shí)下載。CPeerCacheFinder 為前面提到的PeerCache技術(shù)的主控制類。另外,emule還內(nèi)置了一個(gè)IRC客戶端,一個(gè)主要成員函數(shù)都為靜態(tài)的 CPartFileConvert類,能夠?qū)ζ渌姹镜捏H的下載文件進(jìn)行轉(zhuǎn)換。它甚至還提供了一個(gè)自動(dòng)處理zip和rar的類 CArchiveRecovery。

            Kademlia網(wǎng)絡(luò)是emule中相當(dāng)重要的一部分,因此特意把這一部分單獨(dú)拿出來,把它放在這個(gè)小結(jié)之后進(jìn)行描述。

            20. eMule源代碼學(xué)習(xí)心得(20):emule中的Kademlia代碼總體描述
            首先要說一下抱歉,這兩個(gè)星期的事情比較多,所以現(xiàn)在才來寫。不過總算是把這些東西寫完了。而且最后用的代碼是0.47c。

            當(dāng)emule 中開始使用Kademlia網(wǎng)絡(luò)后,便不再會(huì)有中心服務(wù)器失效這樣的問題了,因?yàn)樵谶@個(gè)網(wǎng)絡(luò)中,沒有中心服務(wù)器,或者說,所有的用戶都是服務(wù)器,所有的用 戶也是客戶端,從而完完全全得實(shí)現(xiàn)了P2P。接下來講針對(duì)emule中的Kademlia網(wǎng)絡(luò)進(jìn)行分析,會(huì)有一節(jié)進(jìn)行原理方面的分析。另外的幾節(jié)將會(huì)根據(jù) emule中實(shí)現(xiàn)Kademlia所使用的不同的類分別進(jìn)行講述。其中:

            CKademlia是整個(gè)Kademlia網(wǎng)絡(luò)的主控類,可以直接開始或者停止Kademlia網(wǎng),并且含有Process方法來處理日常事務(wù)。

            CPrefs負(fù)責(zé)處理自身的Kademlia相關(guān)信息,如自身的ID等。

            CRoutingZone,CRoutingBin和CContact三個(gè)類組成了每個(gè)節(jié)點(diǎn)所了解的聯(lián)系信息以及由這些聯(lián)系信息所組成的數(shù)據(jù)結(jié)構(gòu)。

            CKademliaUDPListener負(fù)責(zé)處理網(wǎng)絡(luò)信息。

            CIndexed負(fù)責(zé)處理本地存儲(chǔ)的索引信息。

            CSearch,CSearchManager負(fù)責(zé)處理和搜索有關(guān)的操作,其中前者表示的是一個(gè)單一的搜索任務(wù),后者負(fù)責(zé)對(duì)所有搜索任務(wù)進(jìn)行處理。

            CUInt128負(fù)責(zé)處理一個(gè)128位的長(zhǎng)整數(shù),并且內(nèi)置其各種運(yùn)算。前面已經(jīng)提到過

            21. eMule源代碼學(xué)習(xí)心得(21):emule中的Kademlia的基本原理
            Kademlia 是一種結(jié)構(gòu)化的覆蓋網(wǎng)絡(luò)(Structured Overlay Network),所謂的覆蓋網(wǎng)絡(luò),就是一種在物理的Internet上面再次構(gòu)建的虛擬網(wǎng)絡(luò),所有參與的節(jié)點(diǎn)都知道一部分其它節(jié)點(diǎn)的IP地址,這些節(jié)點(diǎn) 稱為它的鄰居,如果需要查找什么東西,它先在本地尋找,如果找不到,就把這個(gè)查詢轉(zhuǎn)發(fā)到它的鄰居處,希望能夠有可能查找到相應(yīng)的結(jié)果。覆蓋網(wǎng)絡(luò)里面分成了 結(jié)構(gòu)化和非結(jié)構(gòu)化的兩種情況,它們的區(qū)別在于每個(gè)節(jié)點(diǎn)知道哪些其它節(jié)點(diǎn)的信息是否有特定的規(guī)律。在非結(jié)構(gòu)化的覆蓋網(wǎng)中,每個(gè)節(jié)點(diǎn)的鄰居狀況沒有特定的規(guī) 律。因此在非結(jié)構(gòu)化網(wǎng)絡(luò)中,如果要進(jìn)行查詢,會(huì)采取一種叫做泛洪(flooding)的方法,每個(gè)節(jié)點(diǎn)如果在本地沒有查找到想要的結(jié)果,會(huì)把查找請(qǐng)求轉(zhuǎn)發(fā) 到它的鄰居中,然后再通過鄰居的鄰居這種方式來進(jìn)行一步步的查找。但是這種方法如果處理不好,會(huì)造成整個(gè)網(wǎng)絡(luò)的消息負(fù)載過大。已經(jīng)有不少文章對(duì)于優(yōu)化非結(jié) 構(gòu)化覆蓋網(wǎng)絡(luò)中的查詢進(jìn)行了很深入的探討。

            對(duì)于結(jié)構(gòu)化的覆蓋網(wǎng)絡(luò),它的特點(diǎn)是每個(gè)節(jié)點(diǎn)它會(huì)選擇和哪些節(jié)點(diǎn)做鄰居是有一定的規(guī)律的,從而在進(jìn) 行搜索的時(shí)候,節(jié)點(diǎn)把搜索請(qǐng)求進(jìn)行轉(zhuǎn)發(fā)的時(shí)候它能夠通過一定的規(guī)律進(jìn)行選擇把請(qǐng)求轉(zhuǎn)發(fā)到哪些鄰居節(jié)點(diǎn)上。這樣同時(shí)也能減少搜索代價(jià)。結(jié)構(gòu)化的覆蓋網(wǎng)絡(luò)通常 要求每一個(gè)節(jié)點(diǎn)隨機(jī)生成一個(gè)ID,用以判斷各個(gè)節(jié)點(diǎn)之間的關(guān)系。這個(gè)ID和它所在的物理網(wǎng)絡(luò)必須是沒有關(guān)系的。

            對(duì)于Kademlia網(wǎng)絡(luò)來 說,這個(gè)ID是一個(gè)128位的數(shù)值,所有的節(jié)點(diǎn)都用這個(gè)ID來衡量自己與其它節(jié)點(diǎn)的邏輯距離。而邏輯距離的計(jì)算方法就是將兩個(gè)節(jié)點(diǎn)進(jìn)行異或(XOR)操 作。在Kademlia網(wǎng)絡(luò)的形成過程中,每個(gè)節(jié)點(diǎn)選擇鄰居的原則是離自己邏輯距離越近的節(jié)點(diǎn)越有可能被加入到自己的鄰居節(jié)點(diǎn)列表中,具體來說就是在每次 新得到一個(gè)節(jié)點(diǎn)的信息的時(shí)候,是否把它加入到自己的鄰居節(jié)點(diǎn)列表是根據(jù)距離的遠(yuǎn)近來處理的。后面分析具體程序的代碼時(shí)會(huì)有說明。

            結(jié)構(gòu)化的網(wǎng) 絡(luò)的好處就是如果我們要尋找一個(gè)距離某個(gè)ID邏輯距離足夠近的節(jié)點(diǎn),我們可以保證在O(logn)級(jí)別的跳數(shù)找到。只要先尋找自己已知的離目標(biāo)ID邏輯距 離足夠斷的節(jié)點(diǎn),然后再問它知不知道更近的,然后就這樣下去。因此在搜索的時(shí)候也是這樣,當(dāng)需要發(fā)布資源的時(shí)候,把文件進(jìn)行hash,這樣就能夠計(jì)算出一 個(gè)128位的ID,或者把關(guān)鍵字進(jìn)行hash。然后尋找到離這個(gè)結(jié)果邏輯距離最近的節(jié)點(diǎn),把文件或者關(guān)鍵字的信息發(fā)送給它,讓它存起來。當(dāng)有人要搜索同樣 的東西的時(shí)候,由于它用的是同一個(gè)hash算法,因此能夠計(jì)算出對(duì)應(yīng)的ID,并且去搜索那些和這個(gè)ID邏輯距離相近的節(jié)點(diǎn),因?yàn)樗溃绻W(wǎng)絡(luò)中真有這 些資源的話,這些節(jié)點(diǎn)是最有可能知道這些信息的。由此我們可以看出,結(jié)構(gòu)化的網(wǎng)絡(luò)的資源查找效率是很高的,但是它和非結(jié)構(gòu)化的覆蓋網(wǎng)絡(luò)比起來,缺點(diǎn)是不能 進(jìn)行復(fù)雜查詢,即只能通過簡(jiǎn)單的關(guān)鍵字或者文件的hash值進(jìn)行查找。非結(jié)構(gòu)化的網(wǎng)絡(luò)的查找本身就是隨意轉(zhuǎn)發(fā)的,每個(gè)收到的查詢請(qǐng)求的節(jié)點(diǎn)都對(duì)本地的資源 掌握的很清楚,因此自然可以支持復(fù)雜查詢,但是顯然非結(jié)構(gòu)化的網(wǎng)絡(luò)支持的復(fù)雜查詢不太可能動(dòng)員所有的節(jié)點(diǎn)都來做這一動(dòng)作。目前還沒有方法能夠把兩種覆蓋網(wǎng) 絡(luò)的優(yōu)點(diǎn)結(jié)合起來,我也非常想知道這樣的一種方法。

            22. eMule源代碼學(xué)習(xí)心得(22):emule中的Kademlia的基礎(chǔ)設(shè)施類
            Kademlia 的主控類是CKademlia,它負(fù)責(zé)啟動(dòng)和關(guān)閉整個(gè)Kademlia網(wǎng)的相關(guān)代碼。在它的Process函數(shù)中,會(huì)處理和Kademlia網(wǎng)相關(guān)的事 務(wù),例如隔一段時(shí)間檢查某個(gè)區(qū)間的節(jié)點(diǎn)數(shù)是否過少,如果是則尋找一些新的節(jié)點(diǎn)。另外經(jīng)常對(duì)自己的鄰居進(jìn)行檢查等,這些都是屬于需要進(jìn)行日常安排的工作。所 有搜索任務(wù)的日常處理也需要它來調(diào)度。它還作為Kademlia網(wǎng)的代表,向emule其它部分的代碼返回Kademlia網(wǎng)的一些統(tǒng)計(jì)信息。

            另一個(gè)基礎(chǔ)設(shè)施類是CPrefs,它和emule普通代碼中的CPreferences作用類似,但是CPrefs只保留和Kademlia網(wǎng)相關(guān)的,需要長(zhǎng)期保存的本地信息。具體到這個(gè)版本來說,主要就是本地的ID。

            還有一個(gè)很重要的基礎(chǔ)設(shè)施就是CUInt128,實(shí)現(xiàn)對(duì)128位的ID的各種處理,前面的部分已經(jīng)提到。

            23. eMule源代碼學(xué)習(xí)心得(23):emule中的Kademlia的聯(lián)系人列表管理
            CRoutingZone,CRoutingBin和CContact三個(gè)類組成了聯(lián)系人列表數(shù)據(jù)結(jié)構(gòu)。它要達(dá)到我們搜索的要求,即搜索到目標(biāo)的時(shí)間要能夠接受,而且所占用的空間也要能夠接受。

            首 先CContact類包含的是一個(gè)聯(lián)系人的信息,主要包括對(duì)方的IP地址,ID,TCP端口,UDP端口,kad版本號(hào)和其健康程度 (m_byType)。其中健康程度有0-4五個(gè)等級(jí)。剛剛加入的聯(lián)系人,也就是健康狀況未知的,這個(gè)數(shù)值設(shè)置為3。系統(tǒng)會(huì)經(jīng)常通過與各個(gè)聯(lián)系人進(jìn)行聯(lián)系 的方式對(duì)其進(jìn)行健康狀況檢查,經(jīng)常能夠聯(lián)系上的聯(lián)系人,這個(gè)數(shù)值會(huì)慢慢減少到0。而很就沒有聯(lián)系的,這個(gè)數(shù)值會(huì)慢慢增加,如果增加到4后再過一段時(shí)間未能 成功聯(lián)系上的,則將會(huì)被從聯(lián)系人列表中刪除。

            CRoutingBin類包含一個(gè)CContact的列表。這里要注意的是要訪問聯(lián)系人的信息 必須通過某個(gè)CRoutingBin,CRoutingZone內(nèi)部是不直接包含聯(lián)系人信息的。可以把新的聯(lián)系人信息往一個(gè)特定的CRoutingBin 中加,當(dāng)然也可以進(jìn)行聯(lián)系人查找。它也提供方法能夠?qū)ふ页鲭x某個(gè)ID距離最近的聯(lián)系人,并給出這樣的一個(gè)列表。這是相當(dāng)重要的。最后,一個(gè) CRoutingBin類中能夠包含的CContact的數(shù)量也是有限制的。

            CRoutingZone類處于聯(lián)系人數(shù)據(jù)結(jié)構(gòu)的最上層,直接 為Kademlia網(wǎng)提供操作接口。該類的結(jié)構(gòu)為一個(gè)二叉樹,內(nèi)含兩個(gè)CRoutingZone指向它的左子樹和右子樹,另外也包含一個(gè) CRoutingBin類型的指針。但是只有在當(dāng)前的CRoutingZone類為整個(gè)二叉樹的葉節(jié)點(diǎn)時(shí),這個(gè)指向CRoutingBin類型的指針才有 意義。這個(gè)二叉樹的特點(diǎn)是,每個(gè)節(jié)點(diǎn)以下的所有聯(lián)系人的ID都包含一個(gè)共同前綴,節(jié)點(diǎn)的層數(shù)越深,這個(gè)共同前綴越長(zhǎng)。例如,根節(jié)點(diǎn)的左子樹的所有的節(jié)點(diǎn)的 ID一定有一個(gè)前綴"0",而右子樹的所有節(jié)點(diǎn)一定有前綴"1"。同樣,根節(jié)點(diǎn)的左子樹的右子樹下的所有節(jié)點(diǎn)的ID一定有前綴"01",等等,依此類推。 我們?cè)O(shè)想一下節(jié)點(diǎn)不斷得往這個(gè)二叉樹添加的過程。剛開始只有一個(gè)根節(jié)點(diǎn),它也就是葉節(jié)點(diǎn),這時(shí)它內(nèi)部的CRoutingBin是有意義的,當(dāng)聯(lián)系人信息不 斷得被添加進(jìn)去以后,這個(gè)CRoutingBin的容量滿了,這時(shí)要進(jìn)行的就是一個(gè)分裂的操作。這時(shí),會(huì)添加兩個(gè)左子節(jié)點(diǎn)和右子節(jié)點(diǎn),然后把自身的 CRoutingBin中的聯(lián)系人信息按照它們的前綴特點(diǎn)分別復(fù)制往左節(jié)點(diǎn)和右節(jié)點(diǎn),最后把自身的CRoutingBin廢除掉,這樣這個(gè)分裂過程就完 了。當(dāng)分裂完成后,就會(huì)再次試圖添加該聯(lián)系人信息,此時(shí)會(huì)試圖按照它的ID,把它添加到對(duì)應(yīng)的子樹中。但是并不是所有的這種情況節(jié)點(diǎn)都會(huì)發(fā)生分裂,因?yàn)槿? 果允許任意分裂的話,本地所需存儲(chǔ)的節(jié)點(diǎn)信息數(shù)量就會(huì)急劇上升。這里,自身ID的作用就體現(xiàn)了。只有當(dāng)自身ID和當(dāng)前準(zhǔn)備分裂的節(jié)點(diǎn)有共同前綴時(shí),這個(gè)節(jié) 點(diǎn)才會(huì)分裂,而如果判斷到一個(gè)節(jié)點(diǎn)不能分裂,而它的CRoutingBin又滿掉了,那么就會(huì)拒絕添加聯(lián)系人信息。

            我們可以看出,在以上政 策的進(jìn)行下,離自身ID邏輯距離越近(也就是共同前綴越長(zhǎng))的聯(lián)系人信息越有可能被加入,因?yàn)樗鶎?duì)應(yīng)的節(jié)點(diǎn)越有可能因?yàn)榉至讯@得更多的子節(jié)點(diǎn),也就對(duì) 應(yīng)了更多的容量。這樣,在Kademlia網(wǎng)中,每一個(gè)參與者知道的其它參與者信息中,離自己邏輯距離越近的參與者比例越高。由于在搜索的時(shí)候也只需要不 斷得尋找更近的ID,而且每一步都一定會(huì)有進(jìn)展,所以尋找到目標(biāo)ID所需要的時(shí)間上的代價(jià)是O(logn),從這個(gè)二叉樹的結(jié)構(gòu)來看,我們也可以看到,由 于只有部分節(jié)點(diǎn)會(huì)分裂,所以實(shí)質(zhì)上存儲(chǔ)所需要的空間代價(jià)也是O(logn)。

            實(shí)際上CRoutingZone在實(shí)現(xiàn)時(shí)和理論上的Kademlia有一些區(qū)別,如從根節(jié)點(diǎn)開始,有一個(gè)最低分裂層數(shù),也就是說,如果層數(shù)過低的話,是永遠(yuǎn)允許分裂的,這樣它知道的其它地區(qū)的聯(lián)系人信息就能夠稍微多一些。

            24. eMule源代碼學(xué)習(xí)心得(24):emule中的Kademlia網(wǎng)絡(luò)消息處理
            CKademliaUDPListener 負(fù)責(zé)處理所有和Kademlia網(wǎng)相關(guān)的消息。前面已經(jīng)對(duì)emule的通信協(xié)議的基本情況做了一個(gè)大概的描述,我們就可以知道, CKademliaUDPListener處理的消息一定是只和Kademlia網(wǎng)相關(guān)的,分揀工作已經(jīng)在emule的普通UDP客戶端處理代碼那里處理 好了。具體的消息格式前面也有一些介紹,下面會(huì)就一些具體的消息分類做說明。

            首先是健康檢查方面的消息,這樣的消息就是一般的ping- pong機(jī)制。對(duì)應(yīng)的消息有KADEMLIA_HELLO_REQ和KADEMLIA_HELLO_RES。當(dāng)對(duì)本地聯(lián)系人信息列表進(jìn)行檢查時(shí),會(huì)對(duì)它們 發(fā)出KADEMLIA_HELLO_REQ消息,然后處理收到的KADEMLIA_HELLO_RES消息。

            最常用的消息是節(jié)點(diǎn)搜索消息, 在Kademlia網(wǎng)絡(luò)中,進(jìn)行節(jié)點(diǎn)搜索是日常應(yīng)用所需要傳輸?shù)闹饕ⅲ膶?shí)現(xiàn)方式是迭代式的搜索。這種方式就是說當(dāng)開始搜索某個(gè)ID時(shí),在本地聯(lián)系 人信息列表中查找到距離最近的聯(lián)系人,然后向它們發(fā)出搜索請(qǐng)求,這樣通常都能夠得到一些距離更近的聯(lián)系人信息,然后再向它們發(fā)送搜索請(qǐng)求,通過不斷得進(jìn)行 這樣的搜索查詢,就能夠得到距離目標(biāo)ID最近的那些聯(lián)系人信息。這里對(duì)應(yīng)的消息代碼是KADEMLIA_REQ和KADEMLIA_RES。

            接 下來就是對(duì)內(nèi)容進(jìn)行發(fā)布或者搜索。這一點(diǎn)結(jié)合后面的CIndexed類的分析可以知道得更加清楚。emule中存儲(chǔ)在Kademlia網(wǎng)中的信息主要有三 類:文件源,關(guān)鍵字信息和文件的評(píng)論。文件源對(duì)應(yīng)的是每一個(gè)具體的文件,每個(gè)文件都用它的內(nèi)容的hash值作為該文件的唯一標(biāo)示,一條文件源信息就是一條 關(guān)于某人擁有某個(gè)特定的文件的這樣一個(gè)事實(shí)。一條關(guān)鍵字信息則是該關(guān)鍵字對(duì)應(yīng)了某個(gè)文件這樣一個(gè)事實(shí)。很顯然,一個(gè)關(guān)鍵字可能會(huì)對(duì)應(yīng)多個(gè)文件,而一個(gè)特定 的文件的文件源也很有可能不止一個(gè)。但是它們的索引都以固定的hash算法作為依據(jù),這樣使得搜索和發(fā)布都變得很簡(jiǎn)單。

            我們來看發(fā)布過程。 每個(gè)emule客戶端把自己的共享文件的底細(xì)已經(jīng)摸清楚了,在傳統(tǒng)的有中心索引服務(wù)器的場(chǎng)景里,它把自己的所有文件的信息都上傳到中心索引服務(wù)器里。但是 在Kademlia網(wǎng)里,它就需要分散傳播了,它首先做的事情是把文件名進(jìn)行切詞,即從文件名中分解出一個(gè)一個(gè)的關(guān)鍵詞出來,它切詞的方法非常簡(jiǎn)單,就是 在文件名中尋找那些有分割符含義的字符,如下劃線等,然后把文件名切開。計(jì)算出這些關(guān)鍵字的hash值后,它把這些關(guān)鍵字信息發(fā)布到對(duì)應(yīng)的聯(lián)系人那里。并 且把文件信息也發(fā)布到和文件內(nèi)容hash值接近的聯(lián)系人那里。對(duì)應(yīng)的消息是KADEMLIA_PUBLISH_REQ和 KADEMLIA_PUBLISH_RES。另外emule允許用戶對(duì)某個(gè)文件發(fā)表評(píng)論,評(píng)論的信息單獨(dú)保存,但是原理也是一樣的。

            當(dāng)用戶 使用Kademlia網(wǎng)絡(luò)來進(jìn)行搜索并且下載文件的時(shí)候,首先是對(duì)一個(gè)關(guān)鍵詞進(jìn)行搜索,由于使用的是同樣的hash算法,這樣它只要找到ID值和計(jì)算出來 的hash值結(jié)果相近的聯(lián)系人信息后,它就可以直接向它們發(fā)送搜索特定關(guān)鍵詞的請(qǐng)求了。如果得到了返回信息,那么搜索者就知道了這個(gè)關(guān)鍵詞對(duì)應(yīng)了多少文 件,然后把這些文件的信息都列出來。當(dāng)用戶決定下載某個(gè)文件的時(shí)候,針對(duì)這一特定文件的搜索過程就開始了,這一次如果搜索成功,那么返回的就是這個(gè)文件的 文件源信息。這樣emule接下來就只需要按照這些信息去連接相應(yīng)的地址,并且使用傳統(tǒng)的emule協(xié)議去和它們協(xié)商下載文件了。這里對(duì)應(yīng)的消息是 KADEMLIA_SEARCH_REQ和KADEMLIA_SEARCH_RES。

            實(shí)際的實(shí)現(xiàn)中有Kademlia2這種協(xié)議,它的原理 是一樣的,只有協(xié)議代碼和具體的消息格式不一樣,例如KADEMLIA_REQ和KADEMLIA_RES對(duì)應(yīng)了KADEMLIA2_HELLO_REQ 和KADEMLIA2_HELLO_RES,但是后者在具體的消息中包含了比前者豐富一些的信息。在實(shí)現(xiàn)的時(shí)候0.47c更加傾向于使用 Kademlia2,而0.47a更加傾向于使用Kademlia。當(dāng)然,它們兩種協(xié)議都能夠處理。另外,0.47c增加了一個(gè)對(duì)于已發(fā)出的請(qǐng)求的追蹤的 特性,就是一個(gè)包含TrackPackets_Struct類型的列表,這里面詳細(xì)紀(jì)錄了什么時(shí)間曾經(jīng)對(duì)哪個(gè)IP發(fā)出過那種opcode對(duì)應(yīng)的請(qǐng)求。為什 么要這樣呢?這是為了防止針對(duì)DHT的一種路由污染攻擊,因?yàn)樵谒阉髀?lián)系人的時(shí)候,如果搜索到了一些聯(lián)系人信息,也會(huì)試圖把它先加入到本地的聯(lián)系人信息列 表中。這樣如果有人想惡意攻擊的話,它只要不斷得往它想攻擊的emule客戶端發(fā)送KADEMLIA_RES,并且在消息的內(nèi)容中包含大量的虛假聯(lián)系人信 息,就可以使對(duì)方的聯(lián)系人信息列表中充滿垃圾。這樣,由于缺少正確有效的聯(lián)系人信息,它的Kademlia網(wǎng)功能基本上就廢了。而在0.47c里面增加的 這個(gè)特性,就會(huì)對(duì)那種還沒有發(fā)出請(qǐng)求就收到回應(yīng)的情況直接無視,從而避免被愚弄。

            25. eMule源代碼學(xué)習(xí)心得(25):emule中的Kademlia的分布式索引管理
            Kademlia 網(wǎng)絡(luò)的最大的好處是把原來需要存儲(chǔ)到中心索引服務(wù)器中的信息分散存儲(chǔ)到各個(gè)客戶端當(dāng)中,如果要說得更加準(zhǔn)確一點(diǎn),那我們就可以說它把這些信息分散得存儲(chǔ)到 各個(gè)emule客戶端的CIndexed類當(dāng)中。我們可以具體開始看CIndexed的設(shè)計(jì),看它是如何完成這一工作的。在這之前我們要稍微詳細(xì)得說一下 emule發(fā)布到Kademlia網(wǎng)絡(luò)中的信息的各種類型。

            一個(gè)文件源信息是一個(gè)文件內(nèi)容的hash值和擁有這個(gè)文件的客戶端的IP地址, 各種端口號(hào)以及其它信息之間的對(duì)應(yīng)關(guān)系。而一個(gè)關(guān)鍵詞信息則是該關(guān)鍵詞和它對(duì)應(yīng)的文件之間的關(guān)系。在關(guān)鍵詞信息中,它對(duì)應(yīng)的文件信息要更加詳細(xì),通常包括 這個(gè)文件的文件名,文件大小,文件內(nèi)容的hash值,如果是MP3或者其它媒體文件,還會(huì)包含包括作者,生產(chǎn)時(shí)間,文件長(zhǎng)度(這個(gè)長(zhǎng)度是用時(shí)間來衡量的媒 體文件的播放長(zhǎng)度),流派等等tag信息。其中文件內(nèi)容的hash值用來區(qū)分該關(guān)鍵詞對(duì)應(yīng)的不同文件。

            CIndexed中利用了一系列的 Map來存儲(chǔ)這些對(duì)應(yīng)信息,CMap是MFC中實(shí)現(xiàn)標(biāo)準(zhǔn)STL中的map的模板類,CIndexed中包含了四個(gè)這樣的類,分別用來存儲(chǔ)文件源信息,關(guān)鍵 詞信息,文件評(píng)論信息以及負(fù)載信息。其中文件評(píng)論信息是不長(zhǎng)久保存的,而其它的信息都會(huì)在退出的時(shí)候?qū)懙轿募校麓沃匦聠?dòng)emule時(shí)再重新調(diào)入。另 外負(fù)載信息不是等其它聯(lián)系人來發(fā)布的,而是根據(jù)文件源信息和關(guān)鍵詞信息的發(fā)布情況自行進(jìn)行動(dòng)態(tài)調(diào)整的。每一次收到發(fā)布信息時(shí),對(duì)應(yīng)的ID的負(fù)載會(huì)增大,這 一事實(shí)會(huì)在回應(yīng)消息(KADEMLIA_PUBLISH_RES)中體現(xiàn)。

            CIndexed中的信息會(huì)經(jīng)常進(jìn)行檢查,每隔三十分鐘它會(huì)把自 己存儲(chǔ)的所有信息中太老的信息清除掉。其中文件源信息的保存時(shí)間為五小時(shí),關(guān)鍵詞信息為二十四小時(shí),文件評(píng)論的信息保存時(shí)間也為二十四小時(shí)。因此文件的發(fā) 布和關(guān)鍵詞也要周期性得反復(fù)進(jìn)行。其實(shí)這對(duì)于整個(gè)Kademlia網(wǎng)絡(luò)的穩(wěn)定性也是有好處的,因?yàn)槊恳淮温?lián)系都會(huì)試圖把對(duì)方添加到自己的聯(lián)系人列表中,或 者在聯(lián)系人列表中標(biāo)注上一次見到對(duì)方的時(shí)間。

            CIndexed為其它部分的代碼提供了它們所需要的增加信息和搜索信息的接口,這樣在從網(wǎng)絡(luò)中獲取到相關(guān)的搜索或者發(fā)布請(qǐng)求,并且CKademliaUDPListener完成消息的解釋后,就可以交給CIndexed來進(jìn)行處理了。

            26. eMule源代碼學(xué)習(xí)心得(26):emule中的Kademlia搜索任務(wù)管理
            CSearch 和CSearchManager是完成具體搜索任務(wù)的。CSearch對(duì)應(yīng)的是一個(gè)具體的搜索任務(wù),它包括了一個(gè)搜索任務(wù)從發(fā)起到結(jié)束的全部過程,要注意 的是搜索任務(wù)并不只是指搜索文件源或者關(guān)鍵詞的任務(wù),一次發(fā)布任務(wù)它也需要?jiǎng)?chuàng)建一個(gè)CSearch對(duì)象,并且讓它開始執(zhí)行。CSearchManager 則掌握所有的搜索任務(wù),它包含了一個(gè)包含所有CSearch指針對(duì)象的CMap,使用CMap的原因是因?yàn)樗械腃Search都一定對(duì)應(yīng)一個(gè)ID,那個(gè) ID就是該CSearch所對(duì)應(yīng)的目標(biāo),不管是要查找節(jié)點(diǎn),還是要搜索或者發(fā)布信息,一定都要找到和目標(biāo)ID相近的聯(lián)系人。因此 CSearchManager可以使用CMap來表示所有的搜索任務(wù)。

            我們注意到CSearch在創(chuàng)建的時(shí)候就把自己加入到 CSearchManager當(dāng)中。另外CSearch在創(chuàng)建的時(shí)候需要說明它的類型,例如是只是為了搜索節(jié)點(diǎn)還是要搜索關(guān)鍵詞信息或者文件源信息,當(dāng)然 也有可能是發(fā)布文件源信息或者關(guān)鍵詞信息。我們介紹一下CSearch的幾個(gè)方法的作用就可以大概了解CSearch的工作過程。Go是它的啟動(dòng)過程,它 會(huì)開始第一次從本地的聯(lián)系人列表中尋找候選的聯(lián)系人,然后開始發(fā)動(dòng)搜索。SendFindValue的功能就是向某個(gè)聯(lián)系人發(fā)送一個(gè)搜索某ID的聯(lián)系人信 息這樣一個(gè)請(qǐng)求。JumpStart則是在搜索進(jìn)行到一定地步的時(shí)候,如得到了一些中間結(jié)果,開始進(jìn)行下一步的行動(dòng),下一步的行動(dòng)仍然可能是 SendFindValue,也有可能認(rèn)為搜索到的聯(lián)系人離目標(biāo)已經(jīng)足夠近了,于是就可以開始實(shí)質(zhì)性的請(qǐng)求。StorePacket就是這樣一個(gè)實(shí)質(zhì)性的 請(qǐng)求,例如在一個(gè)以發(fā)布文件源為任務(wù)的CSearch中,StorePacket會(huì)向目標(biāo)聯(lián)系人發(fā)送 KADEMLIA2_PUBLISH_SOURCE_REQ(如果不支持Kademlia2,那么是KADEMLIA_PUBLISH_REQ)。最后, CSearch能夠處理各種搜索結(jié)果,然后向調(diào)用它的代碼返回處理好的結(jié)果。

            CSearchManager直接和Kademlia網(wǎng)的其它 部分代碼接觸,例如,如果CKademliaUDPListener搜索到了一些結(jié)果,它會(huì)把這些結(jié)果交給CSearchManager,然后 CSearchManager再去尋找這個(gè)結(jié)果是屬于那個(gè)搜索任務(wù)的,并且進(jìn)行轉(zhuǎn)交。另外CSearchManager對(duì)外提供創(chuàng)建各種新的搜索任務(wù)的接 口,作用類似于設(shè)計(jì)模式中的Factory,其它部分的代碼只需要說明需要開始一個(gè)什么樣的搜索任務(wù)即可,CSearchManager來完成相應(yīng)的創(chuàng)建 CSearch的任務(wù)。

            27. eMule源代碼學(xué)習(xí)心得(27):后記
            終于把自己想干的另一件事情干完了。eMule的代碼寫得 很好,里面有很多都是值得我們學(xué)習(xí)的地方。由于時(shí)間關(guān)系,我也只看了那些和實(shí)現(xiàn)最基本的功能相關(guān)的代碼,而實(shí)際上eMule里面還有很多代碼實(shí)現(xiàn)了很多很 有意思的功能。eMule的GUI設(shè)計(jì)的代碼也是很不錯(cuò)的,但是我也沒有來得及看。最后,歡迎大家來和我討論關(guān)于P2P技術(shù)方面的問題。  

            posted on 2007-10-31 17:03 楊粼波 閱讀(1457) 評(píng)論(0)  編輯 收藏 引用


            只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


            国产成人久久精品激情| 国产精品美女久久久免费| 大香网伊人久久综合网2020| 久久久国产亚洲精品| 99久久成人国产精品免费| 亚洲中文字幕伊人久久无码 | 99久久久精品免费观看国产| 久久久综合香蕉尹人综合网| 97热久久免费频精品99| 久久久这里只有精品加勒比| 国产激情久久久久影院老熟女| 亚洲AV无码1区2区久久| 四虎影视久久久免费观看| 久久国产精品一区二区| 久久人人爽人人爽人人片av高请| 日韩AV毛片精品久久久| 久久93精品国产91久久综合| 高清免费久久午夜精品| 亚洲精品无码久久一线| 欧美一区二区久久精品| 久久久久女教师免费一区| 青青草国产精品久久久久| 国产精品青草久久久久婷婷| 18岁日韩内射颜射午夜久久成人| 亚洲人AV永久一区二区三区久久| 国产—久久香蕉国产线看观看| 四虎国产永久免费久久| 国产精品久久久久无码av| 久久99国内精品自在现线| 精品人妻久久久久久888| 久久久久亚洲AV片无码下载蜜桃 | 久久久久久久久久久精品尤物| 久久亚洲视频| 久久婷婷五月综合色99啪ak| 久久精品成人| 久久综合视频网站| 人妻无码久久精品| 日韩人妻无码一区二区三区久久99| 亚洲精品无码久久久久AV麻豆| 一级做a爰片久久毛片免费陪| 亚洲一区精品伊人久久伊人|