Ctorrent程序源碼分析_第1頁(yè)
Ctorrent程序源碼分析_第2頁(yè)
已閱讀5頁(yè),還剩76頁(yè)未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

CTorrent程序源碼分析姚旭晨目錄CTorrent程序源碼分析 11. 前言 31.1為什么要寫這份文檔 31.2客戶端的選擇 31.3CTorrent簡(jiǎn)介 42.準(zhǔn)備工作 52.1知識(shí)儲(chǔ)備 52.2我對(duì)本篇源碼分析的說明 53.總述 63.1CTorrent的命令行參數(shù)的意義 63.2CTorrent的狀態(tài)欄的意義 63.3各個(gè)類實(shí)現(xiàn)的具體實(shí)例 73.4BT協(xié)議的特性和CTorrent的實(shí)現(xiàn)情況 84.源代碼分析 104.1ctorrent.cpp 104.2downloader.cpp 114.3bencode.h 134.4bitfield.h 154.4.1classBitField 154.5btcontent.h 184.5.1BTCACHE結(jié)構(gòu)體 184.5.2classbtContent 184.6btfiles.h 304.6.1StructBTFILE 304.6.2ClassbtFiles 314.7btrequest.h 354.7.1classRequestQueue 354.7.2classPendingQueue 374.8btstream.h 384.8.1classbtStream 384.9bufio.h 404.9.1classBufIo 404.10connect_nonb.h 424.11httpencode.h 424.12iplist.h 444.12.1struct_iplist 444.12.2classIpList 444.13peer.h 454.13.1宏 454.13.2struct_btstatus 464.13.3classbtBasic 464.13.4classbtPeer:publicbtBasic 474.14peerlist.h 564.14.1struct_peernode 564.14.2classPeerList 574.15rate.h 704.15.1變量 704.15.2函數(shù) 714.16setnonblock.h 714.17sigint.h 714.18tracker.h 724.18.1宏 724.18.2變量 724.18.3函數(shù) 745.后記 795.1開源和BitTorrent,不得不說的話 795.2BT的精神:共享,公平和寬容 795.3本篇文檔的版權(quán)和莫做害群之馬 795.4我的敬意 805.5結(jié)語(yǔ) 80圖表目錄TOC\h\z\c"圖表"圖表1main()函數(shù)流程圖 10圖表2Downloader()函數(shù)流程圖 12圖表3btFiles::_btf_recurses_directory()函數(shù)流程圖 33圖表4btPeer::RequestPiece()函數(shù)流程圖 52圖表5btPeer::Send_ShakeInfo()函數(shù)流程圖 55圖表6PeerList::UnChokeCheck()函數(shù)流程圖 61圖表7算法1流程圖 62圖表8算法3流程圖 63圖表9PeerList::FillFDSET()函數(shù)流程圖 66圖表10PeerList::AnyPeerReady()函數(shù)流程圖 68圖表11btTracker::SendRequest()函數(shù)流程圖 77表格目錄TOC\h\z\c"表格"表格1BitField::Except()函數(shù)邏輯表 16表格2m_shake_buffer[68]位填充情況 19前言1.1為什么要寫這份文檔BitTorrent點(diǎn)對(duì)點(diǎn)文件傳輸協(xié)議(以下簡(jiǎn)稱BT協(xié)議)及其客戶端應(yīng)用大行其道的今天,各種各樣的客戶端不勝枚舉(可以參看/BitTorrentApplications),而各種各樣的BT技術(shù)論壇討論的卻都是有關(guān)客戶端軟件如何使用的問題,有關(guān)底層協(xié)議細(xì)節(jié)和實(shí)現(xiàn)方案的討論少之又少。我碰巧有機(jī)會(huì)研究過一陣BT協(xié)議的原理,也看過一部分源代碼(CTorrent),雖然現(xiàn)在不再繼續(xù)BT方面的研究了,但有感于當(dāng)初看代碼時(shí)遇到的資料的匱乏的窘境,便決心把自己的理解和心得寫出來,算是自己的一份總結(jié)(這也是我的本科畢業(yè)論文),也希望幫助對(duì)BT協(xié)議實(shí)現(xiàn)有興趣的人盡快上手,少走彎路。有關(guān)BT協(xié)議的論述主要有三篇文章:1,BT官方網(wǎng)站上的協(xié)議解釋:/protocol.html。2,BittorrentProtocolSpecification,/BitTorrentSpecification。3,IncentivesBuildRobustnessinBitTorrent,/bittorrentecon.pdf。這三篇文章從不同方面給出了BT協(xié)議從算法到實(shí)現(xiàn)的一個(gè)較為簡(jiǎn)略的描述。為了更深入地理解BT協(xié)議,自己動(dòng)手寫一個(gè)BT客戶端或閱讀一個(gè)BT客戶端的源代碼的工作是必不可少的。1.2客戶端的選擇BramCohen是BT協(xié)議的創(chuàng)建者。根據(jù)這份協(xié)議,他寫了BT的第一個(gè)客戶端,也就是BitTorrent公司的產(chǎn)品:BitTorrent??梢哉f,BitTorrent的源碼和BT協(xié)議是門當(dāng)戶對(duì),要理解協(xié)議,先從BitTorrent的源碼開始是最好不過的了。但BramCohen是用Python語(yǔ)言寫的BitTorrent,這給很多不懂Python的人(我也在內(nèi))帶來了很多麻煩:為了看懂一份源碼而去新學(xué)一份計(jì)算機(jī)編程語(yǔ)言是不是有些不值得呢?好在BT客戶端是如此之多,我們有很大的選擇空間。除了Python,還有Java(主要是Azureus,國(guó)外非常流行的多平臺(tái)的客戶端)和C++(其它大部分客戶端)寫成的程序。經(jīng)過多方比較,我選擇了CTorrent這個(gè)客戶端。雖然CTorrent是用C++寫成的,但僅僅算是一個(gè)輕量級(jí)(light-weighted)的C++軟件。它的庫(kù)函數(shù)依賴型很小,只用到了OpenSSL庫(kù)用來計(jì)算哈希值,所以可以工作在Linux,FreeBSD和MacOS平臺(tái)。CTorrent沒有圖形界面,工作在命令行模式。另外,libtorrent(/products/libtorrent.html)也是一個(gè)值得一看的客戶端。Libtorrent用到了很多C++的模板庫(kù)(主要是boost),客戶端的性能非常好,而且還提供庫(kù)函數(shù)給其它程序調(diào)用。只是作者的C++水平實(shí)在太低,對(duì)這種重量級(jí)的軟件掌握不了。1.3CTorrent簡(jiǎn)介CTorrent是由YuHong寫的一個(gè)BT客戶端。它的代碼大部分都可以看作是C代碼,只是用到了C++的類概念,還有一小部分構(gòu)造函數(shù),析構(gòu)函數(shù),函數(shù)和操作符重載的代碼。不懂C++的人只需有一些C++的基本知識(shí)就完全能看懂源代碼了。CTorrent的主頁(yè)是,它遵循GPL。作者在CTorrent主頁(yè)上稱自己為YuHong,這里有一篇他寫完CTorrent后發(fā)的帖子:/forum/viewtopic.php?p=39082,想必是中國(guó)人吧。用戶在使用時(shí)發(fā)現(xiàn)CTorrent有一些bug,一個(gè)比較明顯的例子是CTorrent下載完成后不會(huì)立即把緩存中的數(shù)據(jù)寫入硬盤,這樣如果按下Ctrl-C結(jié)束程序的話會(huì)造成數(shù)據(jù)的不完整。CTorrent的最新版本是1.3.4(2004年9月7日發(fā)布),作者后面就沒有再發(fā)布新版本,軟件的一些問題也沒有得到修正。雖然有一些bug,但得益于CTorrent是開源項(xiàng)目,很快就有人為CTorrent寫了一些補(bǔ)?。?tracker/?group_id=91688&atid=598034)。其中一個(gè)叫DennisHolmes的人貢獻(xiàn)頗多,他為CTorrent打了很多patch,然后重新發(fā)布,取名為EnhancedCTorrent。EnhancedCTorrent的主頁(yè)是/dholmes/ctorrent。目前已經(jīng)更新到了ctorrent-dhn2版本,這個(gè)版本配合DennisHolmes用Perl寫的一個(gè)CTorrentControlServer,可以實(shí)現(xiàn)對(duì)EnhancedCTorrent運(yùn)行狀況的監(jiān)控。這篇CTorrent的源碼分析是基于ctorrent-dhn1.2版本的,原因是由于我查看EnhancedCTorrent較早,那時(shí)還沒有ctorrent-dhn2版本,再加上自己偷懶,沒有趕在ctorrent-dhn2發(fā)布之前把文章寫完……比較而言,dnh1.2版本已經(jīng)是一個(gè)相對(duì)穩(wěn)定的版本了,dnh2的改進(jìn)主要是在性能方面,而非bugfix(容我再?gòu)?qiáng)詞奪理一句,我簡(jiǎn)略看過dnh2版本的代碼,在dnh1.2的基礎(chǔ)上,看懂dnh2是沒有問題的)。另外,DennisHolmes雖然重新發(fā)布了CTorrent,但他本人對(duì)原作者是極為尊敬的。在他的dnh版本中,原封不動(dòng)地保留了原先代碼的痕跡,自己的改動(dòng)也加上了相應(yīng)的注釋。雖然CTorrent有一些bug,但正如DennisHolmes所言:誰又說其它客戶端沒有bug呢?我的這篇源碼分析也統(tǒng)一稱CTorrent和EnhancedCTorrent為CTorrent,只有在需要兩個(gè)版本比較時(shí)才區(qū)分開來。2.準(zhǔn)備工作2.1知識(shí)儲(chǔ)備要看懂CTorrent源碼和本篇源碼分析,讀者需要具備如下知識(shí):1,前面列舉的BT協(xié)議的大致了解。2,網(wǎng)絡(luò)socket編程方面的基本知識(shí),主要是select()函數(shù)的使用。3,至少會(huì)C語(yǔ)言,了解C++的基本使用方法(主要是類,構(gòu)造函數(shù),析構(gòu)函數(shù)和重載)。2.2我對(duì)本篇源碼分析的說明源代碼中如果出現(xiàn)一些亂碼(特別是在終端中查看時(shí)),設(shè)置:$exportLANG=C即可看到原作者寫的中文注釋。源碼解說一般采取流程圖的形式,有一些函數(shù)的具體功能不是很集中,畫流程圖也表示不出前后聯(lián)系來,就直接寫了步驟分析。有些源碼比較晦澀的,會(huì)直接分析源代碼。源代碼中的全部變量都有分析。大部分函數(shù)都有說明,少數(shù)特別簡(jiǎn)單的函數(shù)和見名知意的函數(shù)沒有說明。源代碼中看似簡(jiǎn)單的表述實(shí)際蘊(yùn)含著及其嚴(yán)格的操作要求(例如宏P(guān)_HANDSHAKE的意思是可以進(jìn)行握手通信了,而不是正在進(jìn)行握手通信或者已經(jīng)完成握手通信了)。所以必須正確理解源代碼各個(gè)宏,變量,函數(shù)的確切含義,才能真正理解程序的流程和作用。分析源碼的最終目的是徹底理解BT協(xié)議的實(shí)現(xiàn)結(jié)構(gòu),以及BT通信性能卓越的原因。雖然程序中涉及BT協(xié)議算法的只有幾個(gè)函數(shù),但這幾個(gè)函數(shù)是在其它大量代碼的基礎(chǔ)上構(gòu)建的。一些有關(guān)種子文件的制作和解析的代碼雖然看似和BT通信關(guān)系不大,但若前面的基礎(chǔ)沒有理解正確,會(huì)給后面的算法分析帶來很大的麻煩。原作者的C語(yǔ)言技巧相當(dāng)高,enjoyit!本文中“函數(shù)”指的是當(dāng)前正在分析的函數(shù),而“程序”指的是整個(gè)CTorrent程序。本文中“消息”指的是peer發(fā)來的固定格式的消息,例如piece消息,bitfield消息等?!皵?shù)據(jù)”指的是客戶端要下載的東西,例如一個(gè)游戲,一段視頻等。英文中種子文件有很多說法,如.torrentfile,metainfofile,本文中均用它們的中文名:種子文件。英文中關(guān)于BT協(xié)議的最小數(shù)據(jù)單元有很多說法,如slice,block,subpiece,本文中使用CTorrent源代碼中的說法:slice。3.總述3.1CTorrent的命令行參數(shù)的意義-h/-H:顯示幫助命令-x:只解碼并顯示種子文件信息,不下載。-c:只檢查已下載的數(shù)據(jù),不下載。-v:打開debug調(diào)試輸出。下載選項(xiàng):-eint 下載完畢后的做種時(shí)間(單位:小時(shí)),默認(rèn)為72小時(shí)。-pport 綁定端口,默認(rèn)為2706。-ssave_as 重命名下載的文件,若是下載的是多個(gè)文件,則sava_as是包含多文件的目錄。-Ccache_size 緩存大小,默認(rèn)為16MB。-f 強(qiáng)制做種模式,不進(jìn)行SHA1HASH檢查。-bbf_filename piece位圖文件名,詳見BitField::SetReferFile()。-Mmax_peers 客戶端最多與多少個(gè)peer通信。-mmin_peers 客戶端至少與多少個(gè)peer通信。-nfile_number 多文件下,選擇哪個(gè)文件去下載(例如第二個(gè)文件file_number就為2)。-Drate 限制最大下載速率(單位:KB/s)。-Urate 限制最大上傳速率(單位:KB/s)。-Ppeer_id 客戶端通信的ID,默認(rèn)為-CD0102-。下載數(shù)據(jù)文件示例:ctorrent-snew_filename-e12-C32-p6881eg.torrent制作種子文件示例:ctorrent-tfile_to_make.avi-sa.torrent-uprotocol://address/announce3.2CTorrent的狀態(tài)欄的意義CTorrent運(yùn)行時(shí)輸出格式如下:$/1/10/40[3/148/148]2MB,1MB|48,20K/s|80各項(xiàng)意義為:/:表明客戶端正在工作的符號(hào),以”-\|/”循環(huán)。1:種子數(shù)目。10:客戶端正在通信的非種子的peer數(shù)目。40:tracker服務(wù)器知道的peer數(shù),也是整個(gè)bt通信群的peer數(shù)。3:客戶端已經(jīng)下載的piece數(shù)目。148:數(shù)據(jù)文件全部的piece數(shù)目。148:客戶端可以得到的piece數(shù)目,若此數(shù)小于全部piece數(shù)目則不會(huì)下載到完整的數(shù)據(jù)。2MB:客戶端已經(jīng)下載的數(shù)據(jù)量。1MB:客戶端正在上傳的數(shù)據(jù)量。48:客戶端的平均下載速率(KB/s)。20:客戶端的平均上傳速率(KB/s)。80:客戶端的即時(shí)下載速率(KB/s)。40:客戶端的即時(shí)上傳速率(KB/s)。0:客戶端與tracker服務(wù)器通信失敗的次數(shù)。1:客戶端與tracker服務(wù)器通信成功的次數(shù)。3.3各個(gè)類實(shí)現(xiàn)的具體實(shí)例CTorrent程序使用了C++面向?qū)ο蟮奶匦?。在程序中有一些類的?shí)例(instance),分別代表了一個(gè)BT通信群中的各個(gè)對(duì)象。3.3.1BTCONTENTBTCONTENT是btContent類實(shí)現(xiàn)的實(shí)例。它在程序中代表種子文件和本地?cái)?shù)據(jù)文件。3.3.2PENDINGQPENDINGQUEUE是PendingQueue類實(shí)現(xiàn)的實(shí)例。它在程序中代表由于與peer的暫時(shí)通信中斷而擱置等待的slice鏈表的隊(duì)列。3.3.3IPQUEUEIPQUEUE是IpList類實(shí)現(xiàn)的實(shí)例。它在程序中代表從tracker服務(wù)器傳來的peer列表的鏈表。3.3.4SelfSelf是btBasic類實(shí)現(xiàn)的實(shí)例。它在程序中代表客戶端自己。3.3.5WORLDWORLD是PeerList類實(shí)現(xiàn)的實(shí)例。它在程序中代表所有正在與客戶端通信的peer的鏈表3.3.6TrackerTracker是btTracker類實(shí)現(xiàn)的實(shí)例。它在程序中代表tracker服務(wù)器。3.4BT協(xié)議的特性和CTorrent的實(shí)現(xiàn)情況BT下載之所以性能出眾是由BT協(xié)議所規(guī)定的一系列機(jī)制所保證的。判斷一個(gè)BT下載軟件性能優(yōu)秀與否則是看這個(gè)軟件對(duì)BT協(xié)議中下載機(jī)制的執(zhí)行情況。BT協(xié)議主要規(guī)定了兩大類機(jī)制保證其性能(詳細(xì)信息請(qǐng)參照”IncentivesBuildRobustnessinBitTorrent”):3.4.1P初始模式(InitialMode):RandomFirstPiece。當(dāng)客戶端剛開始運(yùn)行時(shí),它一個(gè)完整的piece也沒有,這時(shí)需要盡快下載到一個(gè)piece以便可以提供上傳服務(wù)。此時(shí)的算法為:第一個(gè)隨機(jī)piece??蛻舳藭?huì)隨機(jī)找到一個(gè)piece,然后下載。CTorrent隨機(jī)選擇piece,而且更進(jìn)一步采取了一種加速下載的辦法:雖然此時(shí)客戶端沒有piece,但應(yīng)該有向其它peer的申請(qǐng)slice的隊(duì)列了。客戶端只要比較這些隊(duì)列哪個(gè)最短,優(yōu)先下載最短的隊(duì)列即可最快獲得第一個(gè)piece。函數(shù)PeerList::Who_Can_Duplicate()實(shí)現(xiàn)了此算法的代碼。一般模式(NormalMode):StrictPriority和RarestFirst。1,嚴(yán)格優(yōu)先(StrictPriority)一旦某個(gè)slice被申請(qǐng),則這個(gè)slice所在的piece中的其它slice會(huì)先于其它piece的slice被申請(qǐng)。這樣做可以盡量使最先申請(qǐng)的piece最先被下載完畢。這條規(guī)則看似簡(jiǎn)單而且公平,但實(shí)現(xiàn)起來非常困難:BT協(xié)議規(guī)定一個(gè)piece中的多個(gè)slice可以向多個(gè)peer申請(qǐng),而客戶端又同時(shí)從多個(gè)peer處申請(qǐng)了多個(gè)piece中的slice,這么多數(shù)據(jù)傳輸隊(duì)列同時(shí)進(jìn)行,要保證嚴(yán)格優(yōu)先是非常困難的。CTorrent在設(shè)計(jì)時(shí)采取了一種比較簡(jiǎn)單的方法:一旦向某個(gè)peer申請(qǐng)了某個(gè)slice,則這個(gè)piece中的所有slice均向這個(gè)peer申請(qǐng)。為了保證盡快將一個(gè)piece下載完成,CTorrent會(huì)找出當(dāng)前正在與之通信的那個(gè)peer(正在通信的peer通常比較活躍),然后把所有slice請(qǐng)求隊(duì)列中最慢的那個(gè)隊(duì)列找出來,交給這個(gè)peer去下載。函數(shù)PeerList::Who_Can_Abandon()實(shí)現(xiàn)了此算法的代碼。2,最少優(yōu)先(RarestFirst)客戶端下載時(shí)選擇所有peer擁有最少的那個(gè)piece優(yōu)先下載。函數(shù)BitField::Random()是有關(guān)piece選擇機(jī)制的代碼,但它只是隨機(jī)選擇了piece,沒有實(shí)現(xiàn)最少優(yōu)先。結(jié)束模式(EndgameMode)由于每一個(gè)piece只向一個(gè)peer申請(qǐng),當(dāng)peer數(shù)大于還沒有申請(qǐng)的piece數(shù)時(shí),客戶端便進(jìn)入了結(jié)束模式。此時(shí)客戶端可以向所有的peer發(fā)送還沒有下載完畢的slice的請(qǐng)求,以便盡快下載完畢好做種子。CTorrent變相實(shí)現(xiàn)了這個(gè)算法,它會(huì)找出當(dāng)前正在與之通信的那個(gè)peer(正在通信的peer通常比較活躍),然后把所有slice請(qǐng)求隊(duì)列中最長(zhǎng)的那個(gè)隊(duì)列找出來,交給這個(gè)peer去下載。函數(shù)PeerList::Who_Can_Duplicate()實(shí)現(xiàn)了此算法的代碼。函數(shù)PeerList::Who_Can_Duplicate()和PeerList::Who_Can_Abandon()的調(diào)用環(huán)境均是函數(shù)btPeer::RequestPiece(),應(yīng)將這三個(gè)函數(shù)一起查看才能清楚piece選擇機(jī)制的實(shí)現(xiàn)。3.4.2Choking算法Choking,Unchoking,OptimisticUnchoking三個(gè)算法是保證BT下載公平的基石,即“一報(bào)還一報(bào)”,“人人為我,我為人人”。具體實(shí)現(xiàn)請(qǐng)參照PeerList::UnChokeCheck()??偟膩碚f,CTorrent程序簡(jiǎn)單而巧妙地實(shí)現(xiàn)了BT協(xié)議中的保證下載性能的核心算法。只是最少優(yōu)先算法沒有實(shí)現(xiàn),會(huì)給BT通信群的穩(wěn)定性帶來一定的影響。不過,這個(gè)問題已經(jīng)在CTorrent-dnh2版本中得到了改正和優(yōu)化。點(diǎn)對(duì)點(diǎn)(PeertoPeer)通信是BT協(xié)議最大的特色,它充分利用了互聯(lián)網(wǎng)上各個(gè)下載終端的帶寬,使得由服務(wù)器上傳速率有限所帶來的瓶頸問題得以解決。但除此以外,BT協(xié)議本身還通過piece選擇機(jī)制優(yōu)化了下載:由于數(shù)據(jù)以slice的形式分塊下載,一般一個(gè)slice只有32KB,即使所有的peer的上傳速率都很慢,但slice是如此之小,以至于從一個(gè)很慢的peer處獲得一個(gè)slice所需時(shí)間也極少,BT客戶端只需合理安排對(duì)slice的請(qǐng)求,在較活躍的peer和下載較慢的slice中作出相應(yīng)的調(diào)整和搭配,即可獲得較高的下載速率。打個(gè)比方,火車站檢票口處會(huì)有多個(gè)檢票員(peer)在檢票。每個(gè)人(slice)手里都拿著一張票,排成很多條并排的隊(duì)伍(slice隊(duì)列)等候檢票。由于檢票員的檢票速度有快有慢,控制中心(客戶端程序)只需適時(shí)作出調(diào)整,將較長(zhǎng)的隊(duì)列分配給較快的檢票員即可做到全體乘客的快速通過??偨Y(jié)起來,BT協(xié)議的精髓便是通過化整為零,主動(dòng)選擇來充分利用各個(gè)下載終端的帶寬,配合以相應(yīng)的公平機(jī)制,保證整個(gè)BT通信群的高性能和高穩(wěn)定性。4.源代碼分析4.1ctorrent.cppCTorrent客戶端的主程序,主要負(fù)責(zé)解析用戶參數(shù),然后建立磁盤文件并初始化和tracker服務(wù)器的連接,最后調(diào)用downloader()函數(shù)進(jìn)入一個(gè)數(shù)據(jù)處理的大循環(huán)。流程圖如下:開始開始設(shè)置產(chǎn)生隨機(jī)數(shù)的種子若只制作種子而不下載數(shù)據(jù):由硬盤數(shù)據(jù)初始化BTCONTENT,制作種子文件,退出程序Random_init()由種子文件初始化BTCONTENTBTCONTENT.InitialFromMI(arg_metainfo_file,arg_save_as)BTCONTENT.CreateMetainfoFile()初始化傾聽套接字。BTCONTENT.InitialFromFS()WORLD.Initial_ListenPort()初始化與tracker服務(wù)器的連接進(jìn)入傳送數(shù)據(jù)的大循環(huán)Tracker.Initial()BTCONTENT.FlushCache()循環(huán)退出后將緩沖區(qū)數(shù)據(jù)寫入磁盤。Downloader()程序結(jié)束圖表SEQ圖表\*ARABIC1main()函數(shù)流程圖4.2downloader.cpp此文件只有一個(gè)程序:Downloader(),負(fù)責(zé)客戶端與tracker服務(wù)器的通信,與peer的數(shù)據(jù)交換等,函數(shù)具體的實(shí)現(xiàn)則是通過調(diào)用各司其職的其它函數(shù)實(shí)現(xiàn)的。開始開始檢查做種時(shí)間系統(tǒng)調(diào)用select()監(jiān)視文件描述符集等待數(shù)據(jù)WORLD.FillFDSET(&now,&rfd,&wfd)根據(jù)當(dāng)前peer狀態(tài)將它們放入可讀或可寫文件描述符集中Tracker.IntervalCheck(&now,&rfd,&wfd);檢查與tracker服務(wù)器的通信間隔和peer數(shù)BTCONTENT.SeedTimeout(&now)Tracker.SocketReady(&rfd,&wfd,&nfds)Peer有數(shù)據(jù)到達(dá)tracker服務(wù)器有數(shù)據(jù)到達(dá)WORLD.AnyPeerReady(&rfd,&wfd,&nfds)與tracker服務(wù)器的通信結(jié)束了?結(jié)束是否圖表SEQ圖表\*ARABIC2Downloader()函數(shù)流程圖4.3bencode.h此文件提供了一系列種子文件編解碼的函數(shù),其中編碼函數(shù)較為簡(jiǎn)單,解碼函數(shù)比較晦澀:size_tbuf_int(constchar*b,size_tlen,charbeginchar,charendchar,size_t*pi)beginchar非空時(shí),解析i<integerencodedinbasetenASCII>e型表示的整數(shù)。例如:b='i123e',len=5,beginchar='i',endchar='e',*pi=123,return=5。beginchar為0時(shí),解析<stringlengthencodedinbasetenASCII>:<stringdata>型表示的字符串。例如,b=”4:spam”,len=6,beginchar=0,endchar=‘:’,*pi=4,返回值為2(’:’和’4’size_tbuf_str(constchar*b,size_tlen,constchar**pstr,size_t*slen)解析<stringlengthencodedinbasetenASCII>:<stringdata>型表示的字符串。返回值根據(jù)pstr和slen發(fā)生變化。例如:b="12:announce_url"若*pstr="announce_url",函數(shù)將*slen賦值為12,返回值無實(shí)際意義。若傳遞參數(shù)*pstr=0,*slen=0,則返回值為整個(gè)字符串的長(zhǎng)度,即15。size_tdecode_int(constchar*b,size_tlen)調(diào)用buf_int(),返回整個(gè)以類似i<integerencodedinbasetenASCII>e表示的字符個(gè)數(shù)(’i’和’e’均計(jì)算在內(nèi))。size_tdecode_str(constchar*b,size_tlen)調(diào)用buf_str(),返回整個(gè)字符串的長(zhǎng)度。size_tdecode_dict(constchar*b,size_tlen,constchar*keylist)解析種子文件中dictonary用的,由于整個(gè)種子文件就是一個(gè)dictonary,而這個(gè)大的dictonary中還有小的dictonary,所以隨著keylist的不同,函數(shù)會(huì)用不同的方法解析dictonary。若kyelist=”announce”,則函數(shù)會(huì)返回位于”announce”后面的數(shù)的位置,如圖,為0x0b。若keylist=”info|length”,這表明查詢info這個(gè)dictonary中的”length”后面的數(shù)的位置。如圖,為0x54。若keylist=(char*)0,則返回整個(gè)dictonary的長(zhǎng)度(從’d’到’e’)。size_tdecode_list(constchar*b,size_tlen,constchar*keylist)返回整個(gè)list的長(zhǎng)度。例如=“l(fā)4:spam4:eggse”,則返回16。size_tdecode_rev(constchar*b,size_tlen,constchar*keylist)根據(jù)*b的數(shù)值分別調(diào)用decode_int(),decode_dict(),decode_list(),decode_str()函數(shù)解析。size_tdecode_query(constchar*b,size_tlen,constchar*keylist,constchar**ps,size_t*pi,intmethod)此函數(shù)負(fù)責(zé)解析是哪個(gè)宏函數(shù)(meta_str(),meta_int(),meta_pos())調(diào)用了它,并由解析結(jié)果去調(diào)用相應(yīng)的函數(shù)。size_tdecode_list2path(constchar*b,size_tn,char*pathname)該函數(shù)只在多文件情況下被調(diào)用,將以lists形式表示的多個(gè)文件寫入pathname。函數(shù)每次只寫入一個(gè)文件名,需要多次被調(diào)用才能將所有文件名解析出來。size_tbencode_buf(constchar*buf,size_tlen,FILE*fp)以如下形式將str寫入文件fp:<stringlengthencodedinbasetenASCII>:<stringdata>例如,str=”spam”,則函數(shù)將”4:spam”(引號(hào)不包括)寫入文件fp。size_tbencode_str(constchar*str,FILE*fp)調(diào)用bencode_buf()向文件中寫入str的長(zhǎng)度和str。size_tbencode_int(constintinteger,FILE*fp)以如下形式將整數(shù)integer寫入文件:i<integerencodedinbasetenASCII>e例如,integer=19,則函數(shù)將”i19e”(引號(hào)不包括)寫入文件fp。size_tbencode_begin_dict(FILE*fp)向文件fp中寫入字符’d’,作為dictornary的開始。Dictornary結(jié)束后必須調(diào)用bencode_end_dict_list()寫入字符’e’。size_tbencode_begin_list(FILE*fp)向文件fp中寫入字符’l’,作為list的開始。list結(jié)束后必須調(diào)用bencode_end_dict_list()寫入字符’e’size_tbencode_end_dict_list(FILE*fp)向文件fp中寫入字符’e’,作為dictionary或list的結(jié)束。size_tbencode_path2list(constchar*pathname,FILE*fp)該函數(shù)只在多文件情況下被調(diào)用,pathname里儲(chǔ)存了一個(gè)文件信息的鏈表。此函數(shù)將所有的文件名以lists的形式寫入文件fp。4.4bitfield.h4.4.1classBitFieldBitField類是一個(gè)位圖。Piece以從小到大的索引順序在位圖unsignedchar*b中占有1位(bit)。4.4.1變量staticsize_tnbitspiece總數(shù)。staticsize_tnbytes位圖區(qū)域b的大小。若共有65個(gè)piece,則nbytes=9。即b占9個(gè)字節(jié),但是只有前65位有意義。unsignedchar*b位圖所在的內(nèi)存區(qū)域,以字符串形式表示。size_tnset已經(jīng)獲得的piece數(shù),也就是b中被置位的位數(shù)。當(dāng)nset==nbits時(shí),文件的所有piece全部獲得。4.4.2函數(shù)voidBitField::SetAll()為b開辟內(nèi)存區(qū)域,并使nset=BitField::SetReferFile(constchar*fname)此函數(shù)在用戶指定piece位圖時(shí)使用。假定用戶在硬盤上有一文件,其內(nèi)容是要下載的數(shù)據(jù)文件的piece位圖。使用”-b”參數(shù)將此文件名傳給fname。SetReferFile()便會(huì)讀取位圖文件,并調(diào)用SetReferBuffer()將位圖文件內(nèi)容存儲(chǔ)在BTCONTENT.pBF中。使用”-b”參數(shù)會(huì)使用用戶指定的位圖文件而非程序自己計(jì)算位圖,一個(gè)bit之差都會(huì)導(dǎo)致下載失敗,所以作者提醒用戶小心使用。voidBitField::Set(size_tidx)將b中idx對(duì)應(yīng)的位置為1,并更新nset的值。voidBitField::UnSet(size_tidx)將idx對(duì)應(yīng)的位置置為0。注意由于SetAll只是開辟了內(nèi)存區(qū)域,并沒有將b全部置為1,所以Unset()函數(shù)還要調(diào)用_isfull()檢查b是否已滿,若是,則將b全部置為1,然后再將idx位置為0。intIsSet(size_tidx)const;查看第idx個(gè)piece在piece位圖中是否已經(jīng)存在。size_tBitField::Random()const函數(shù)隨機(jī)選擇BitField位圖中存在的一個(gè)piece的索引idx,并返回idx。程序中只有一處調(diào)用此函數(shù):idx=tmpBitField2.Random();tmpBitField2是客戶端程序還沒有向peer請(qǐng)求的所有piece的位圖。上面調(diào)用的目的是隨機(jī)選擇一個(gè)索引為idx的piece,然后向peer發(fā)送request信息。但是函數(shù)Random()設(shè)計(jì)得并不好,原因是它沒有體現(xiàn)出來BT協(xié)議中的“最少優(yōu)先”原則。當(dāng)很多peer在下載同一個(gè)數(shù)據(jù)文件時(shí),總有一些piece是大部分peer都有的,而另一些piece只有少部分peer有??蛻舳顺绦蛟谶x擇piece下載時(shí)應(yīng)優(yōu)先選擇所有peer擁有最少的那個(gè)piece,這樣一來可以防止某個(gè)擁有唯一piece的peer突然退出而導(dǎo)致整個(gè)BT通信群下載的失敗,二來可以將piece平均分布在各個(gè)peer中加快下載速率。這樣一種選擇piece下載的機(jī)制便叫“最少優(yōu)先”(rarestfirst)原則。很明顯,函數(shù)只是在所有沒有發(fā)出請(qǐng)求的piece種隨機(jī)選擇了一個(gè)piece而沒有做到最少優(yōu)先,如果一個(gè)BT通信群中有很多這樣的客戶端程序,那么這個(gè)BT通信群將是比較脆弱的。voidBitField::Comb(constBitField&bf)函數(shù)計(jì)算并設(shè)置this.nset為this和bf加起來全部的piece數(shù)(共有的只算1個(gè)),并更新piece位圖this為兩者全部的piece位圖。若this或bf已滿,則調(diào)用SetAll()設(shè)置this.nset為nbits,否則,調(diào)用_recalc()計(jì)算。voidBitField::Except(constBitField&bf)如果piece位圖b中有某一個(gè)piece(即相應(yīng)位被置1),而bf.b中沒有,那么b的相應(yīng)位被置1。除此以外的任何其它情況,b的相應(yīng)位都被置0。b有此piece?bf.b有此piece?函數(shù)調(diào)用后位圖b的相應(yīng)位:101110000010表格SEQ表格\*ARABIC1BitField::Except()函數(shù)邏輯表注意此函數(shù)中有一個(gè)較易混淆的地方,那就是“bf.b中沒有”此piece的真正含義。由于很多情況都把pBFilter傳給bf,例如:tmpBitField.Except(*BTCONTENT.pBFilter);而根據(jù)pBFilter的表示方式,若pBFilter某一位被置0,則表明“有”此piece,即需要下載這個(gè)piece。所以,應(yīng)根據(jù)bf的具體表示方式來判斷函數(shù)的作用。voidBitField::Invert()若b為空,函數(shù)為b重新開辟內(nèi)存,并使nset與nbit相等,表示b已滿。若b已滿,函數(shù)將b全部置為0,并使nset等于0,表示b為空。若兩者皆非,則函數(shù)將b里的有意義位按位取反,并重新設(shè)置BitField::WriteToFile(constchar*fname)程序中數(shù)次出現(xiàn)這樣的調(diào)用:if(arg_bitfield_file)BTCONTENT.pBF->WriteToFile(arg_bitfield_file);當(dāng)用戶通過”-b”參數(shù)指定硬盤piece位圖文件名稱時(shí),程序會(huì)調(diào)用WriteToFile()將BTCONTENT.pBF(也就是piece位圖)寫入硬盤。如果用戶在指定了”-b”的同時(shí)還使用”-c”參數(shù)令程序只檢查硬盤上的已下載文件而不實(shí)際下載文件,程序會(huì)在檢查完文件后將piece位圖以文件名arg_bitfield_file寫入硬盤。voidBitField::SetReferBuffer(char*buf)拷貝表示piiece位圖的buf到b中,并重新計(jì)算位圖中已擁有的piece個(gè)數(shù)。voidBitField::WriteToBuffer(char*buf)如果piece位圖已滿,則將buf全部置為1。否則,拷貝位圖到buf中。inlinevoidBitField::_setall(unsignedchar*buf)將buf中有意義的區(qū)域置為1。所謂有意義是指可以buf所代表的位圖中可以表示piece的位。若piece數(shù)目不能剛好被8整除,buf中最有會(huì)有幾位不代表任何piece,它們一直為0。inlinevoidBitField::_set(size_tidx)函數(shù)將idx對(duì)應(yīng)的位置為1。注意此函數(shù)并不重新設(shè)置nset,所以只能被已經(jīng)設(shè)置好nset的函數(shù)(例如Invert())調(diào)用。inlinevoidBitField::_recalc()由位圖b重新計(jì)算nset的值4.5btcontent.h4.5.1BTCACHE結(jié)構(gòu)體為提高磁盤性能,類btContent維護(hù)一個(gè)BTCACHE鏈表,缺省為16MB。客戶端程序從磁盤讀的數(shù)據(jù)或想要向磁盤寫的數(shù)據(jù)會(huì)先放到BTCACHE鏈表中,直至鏈表滿了才寫入磁盤。u_int64_tbc_off此BTCACHE鏈表成員的絕對(duì)偏移地址。size_tbc_len此BTCACHE鏈表成員的長(zhǎng)度。每個(gè)成員的長(zhǎng)度不一定相同,取決于客戶端想讀或?qū)懙臄?shù)據(jù)量。unsignedcharbc_f_flush:1flush標(biāo)志,若為1則此BTCACHE鏈表成員中的數(shù)據(jù)會(huì)被寫入磁盤。若為0則表示此數(shù)據(jù)是從磁盤讀出的。unsignedcharbc_f_reserved:7保留項(xiàng),用途不詳。time_tbc_last_timestamp最后一次讀或?qū)懀ㄓ蒪c_f_flush為1或0決定)的時(shí)間。char*bc_buf存儲(chǔ)的數(shù)據(jù)。struct_btcache*bc_next下一個(gè)節(jié)點(diǎn)。4.5.2classbtContentbtContent是有關(guān)數(shù)據(jù)文件和種子文件的類。很多變量以m開頭,我認(rèn)為m代表了metainfo,也就是俗稱的.torrent“種子”文件。具體來說,它存儲(chǔ)了以下一些數(shù)據(jù):變量char*m_announcem_announce存儲(chǔ)了tracker服務(wù)器的announceURL,例如:”11/announce”。如果用戶要制作種子文件,則必須指定m_announce的值。如果用戶要根據(jù)種子文件下載數(shù)據(jù),那么m_announce便存儲(chǔ)了種子文件中的announceURL。unsignedchar*m_hash_table,size_tm_hashtable_lengthm_hash_table存儲(chǔ)了所有piece的SHA1hash值,它的長(zhǎng)度是m_hashtable_length。size_tm_piece_length,size_tm_npiecesm_piece_length是piece的長(zhǎng)度,制作種子文件時(shí)由用戶指定,一般為256KB。m_npieces則是所有piece的總數(shù)了。一般最后一個(gè)piece的長(zhǎng)度會(huì)小一點(diǎn)。由于SHA1hash的長(zhǎng)度為20,所以有以下關(guān)系:m_npieces=m_hashtable_length/20例如,數(shù)據(jù)文件為1000KB,假設(shè)piece長(zhǎng)度定為256KB,那么m_npieces=4,m_hashtable_lenth=4×20=80。unsignedcharm_shake_buffer[68]m_shake_buffer存儲(chǔ)了client和tracker或peer握手時(shí)的數(shù)據(jù)。詳見BitTorrentSpecification。Ctorrent的m_shake_buffer一般為以下數(shù)值:位數(shù)01……………….1920….2728….4748……5556….67填充19BitTorrentprotocol0計(jì)算值-CD0102-隨機(jī)數(shù)解釋握手信息使用的協(xié)議,為”BitTorrentprotocol”,不算引號(hào),共19位協(xié)議保留位,全部填充為0。種子文件的SHA1HASH值,共20位最后20位為PeerID。前面是用戶程序的名稱(CD)和版本號(hào)(0102),以“-”開頭和結(jié)尾,后面是隨機(jī)數(shù),一般為程序啟動(dòng)時(shí)產(chǎn)生的隨機(jī)數(shù)。這20位唯一地確定了bt客戶端的名稱。表格SEQ表格\*ARABIC2m_shake_buffer[68]位填充情況CTorrent本來的PeerID是-CTorrent-,但是因?yàn)樵镜腃Torrent程序被很多客戶端認(rèn)為bug比較多(“buggy”),它們?cè)赑2P通信時(shí)一旦發(fā)現(xiàn)對(duì)方客戶端是CTorrent,就干脆采取了放棄的方式,所以EnhancedCTorrent將其peerID改為了CD0102,代表“CtorrentDnh1.2”time_tm_create_date,m_seed_timestamp,m_start_timestamp制作種子文件時(shí),m_create_date自然是制作時(shí)間了。下載數(shù)據(jù)文件時(shí),m_create_date是種子文件里“creationdate”項(xiàng)的數(shù)值,用的是標(biāo)準(zhǔn)UNIX計(jì)時(shí)(1970年1月1日0點(diǎn)到現(xiàn)在的秒數(shù))。m_start_timestamp是客戶端程序啟動(dòng)的標(biāo)準(zhǔn)UNIX計(jì)時(shí)。當(dāng)下載完成后,Ctorrent會(huì)給出下載使用的全部時(shí)間。當(dāng)下載完成后,客戶端便由下載者變?yōu)榱松蟼髡撸ㄟ@兩個(gè)詞有多種說法:下載者-種子,downloader-uploader,leecher-seeder),此時(shí)m_seed_timestamp被設(shè)置。程序需要記錄這個(gè)時(shí)間以便在缺省做種時(shí)間(72小時(shí))完畢后退出。u_int64_tm_left_bytes客戶端缺少的字節(jié)數(shù)。程序剛運(yùn)行時(shí)m_left_bytes就是數(shù)據(jù)文件的字節(jié)數(shù),然后每獲得一個(gè)piece,m_left_bytes便減去piece_length,直到下載完畢開始做種,此時(shí)為0。btFilesm_btfilesm_btfiles儲(chǔ)存了種子文件中列出的供用戶下載的數(shù)據(jù)文件的信息。具體請(qǐng)見btFiles類。BTCACHE*m_cache詳見BTCACHE結(jié)構(gòu)體。size_tm_cache_size,m_cache_usedm_cache_size為BTCACHE結(jié)構(gòu)體鏈表中能儲(chǔ)存的最大數(shù)據(jù)量,也就是緩存大小,一般為16MB。m_cache_used是已用緩存數(shù)。BitField*pBF要下載的數(shù)據(jù)文件的piece位圖。每下載成功一個(gè)piece,將相應(yīng)位(bit)置1。表明客戶端已經(jīng)擁有此piece。BitField*pBFilter要下載的文件的過濾器,也是piece位圖。在多文件情形下,用戶可能會(huì)只選擇自己需要的文件下載。此時(shí)程序會(huì)調(diào)用btContent::SetFilter()將需要下載的piece在位圖中所對(duì)應(yīng)的位置0。程序只下載pBFilter中置0的piece。char*global_piece_buffer一個(gè)piece長(zhǎng)度的數(shù)據(jù)緩沖區(qū),函數(shù)調(diào)用btContent::ReadPiece()或btContent::ReadSlice()從磁盤讀取數(shù)據(jù)時(shí)會(huì)將數(shù)據(jù)放入global_piece_buffer中。函數(shù)intbtContent::InitialFromFS(constchar*pathname,char*ann_url,size_tpiece_length)當(dāng)用戶要制作種子文件時(shí),程序調(diào)用InitialFromFS,表示從本地獲取數(shù)據(jù)文件,并通過計(jì)算文件大小,SHA1Hash值等將btContent中的變量初始化。具體來說,此函數(shù)的執(zhí)行步驟為:設(shè)置m_piece_length,m_announce,m_create_date。調(diào)用函數(shù)BuildFromFS()設(shè)置m_btfiles。由計(jì)算得到的m_piece_length為global_piece_buffer開辟內(nèi)存空間。計(jì)算m_npieces,m_hashtable_length。調(diào)用GetHashValue()設(shè)置m_hash_table。制作種子文件時(shí)用戶很有可能看到這樣的輸出:Createhashtable(alreadypieces/totalpieces):18/19Complete.顯示18/19后卻加了一個(gè)Complete,到底有沒有完成呢?這是InitialFromsFS()在最后顯示上的小bug:percent=m_npieces/100;if(!percent)percent=1;for(n=0;n<m_npieces;n++){if(GetHashValue(n,m_hash_table+n*20)<0)return-1;if(0==n%percent){printf("\rCreatehashtable(alreadypieces/totalpieces):%u/%u",n,m_npieces);fflush(stdout);}}printf("Complete.\n");假設(shè)文件被分成19個(gè)piece,即m_npiece為19。那么percent為1。由于piece的索引從0開始,當(dāng)n=18時(shí)Hash表已經(jīng)制作完了,所以當(dāng)n=19時(shí)for循環(huán)退出直接顯示complete了。改正的方法很簡(jiǎn)單,在printf("Complete.\n");前加一句:printf("\rCreatehashtable(alreadypieces/totalpieces):%u/%u",m_npieces,m_npieces);即可。intbtContent::GetHashValue(size_tidx,unsignedchar*md)此函數(shù)將以idx為索引的piece讀入global_piece_buffer中(ReadPiece()),計(jì)算SHA1Hash值(Sha1()),并將此值儲(chǔ)存在md中。ssize_tbtContent::ReadPiece(char*buf,size_tidx)此函數(shù)實(shí)際是調(diào)用了ReadSlice將以idx為索引的piece讀入buf中。ssize_tbtContent::ReadSlice(char*buf,size_tidx,size_toff,size_tlen)此函數(shù)的作用是將第idx個(gè)(索引從0開始)piece中以off為偏移,len長(zhǎng)度的數(shù)據(jù)讀入buf中。剛進(jìn)入函數(shù)時(shí),有一判斷:if(!m_cache_size)returnm_btfiles.IO(buf,offset,len,0);else{…}當(dāng)用戶僅僅只想制作種子文件時(shí),并不需要m_cache,因此也就沒有調(diào)用CacheConfigure()將m_cache_size賦新值。函數(shù)直接調(diào)用m_btfiles.IO(),將數(shù)據(jù)讀出。若m_cache_size已被配置(缺省為16MB),則函數(shù)除了將數(shù)據(jù)讀入buf中,還會(huì)調(diào)用btContent::CacheIO()將數(shù)據(jù)放入BTCACHE*m_cache鏈表中。voidbtContent::CacheConfigure()配置硬盤緩存m_cache_size大小,缺省為16MB。也可由用戶指定,但最大為128MB。intbtContent::CreateMetainfoFile(constchar*mifn)此函數(shù)用來制作種子文件,并將文件名保存為mifn。有關(guān)種子文件的編碼規(guī)范,請(qǐng)參照BitTorrentSpecificationv1.0。在這里我們舉一個(gè)例子來說明CreatMetainfoFile()如何制作種子文件。取源文件testdata(4841860B),假設(shè)保存為a.torrent種子文件,使用如下命令:$ctorrent-ttestdata-sa.torrent-uprotocol://address/announce用vi打開種子文件,命令::%!xxd將其轉(zhuǎn)換為16進(jìn)制形式(:%!xxd–r反轉(zhuǎn)換),顯示內(nèi)容(保存后才會(huì)有高亮顯示)如下::%!xxd的意思是對(duì)整個(gè)(%)文件執(zhí)行外部(!)命令(xxd)。注意最后值為0x0a的點(diǎn)號(hào)’.’,這是xxd程序自己加上去的,不是種子文件中有的。函數(shù)運(yùn)行步驟如下:使用fopen()檢測(cè)種子文件a.torrent是否已經(jīng)存在,若沒有則使用可寫方式創(chuàng)建它。調(diào)用函數(shù)size_tbencode_begin_dict(FILE*fp)向文件中插入字符’d’,表示dictonary。Dictonary要以’e’結(jié)尾,注意上圖中倒數(shù)第二個(gè)字符(位于020d位置),也就是最后一個(gè)‘e’,便是此結(jié)尾。調(diào)用函數(shù)bencode_str("announce",fp)向文件中寫入入字符串,表示8個(gè)字符長(zhǎng)度的announce后面要跟一個(gè)tracher服務(wù)器的announce地址。調(diào)用函數(shù)bencode_str(m_announce,fp)將m_announce(“protocol://address/announce”)寫入文件。調(diào)用函數(shù)bencode_str("creationdate",fp)和bencode_int(m_create_date,fp)向文件中寫入長(zhǎng)為13的字符串”creationdate”,然后在將以標(biāo)志UNIX計(jì)時(shí)表示的文件創(chuàng)建時(shí)間m_create_date寫入。BT協(xié)議規(guī)定整數(shù)以’i’開頭以’e’結(jié)束。即:i1142146385e。調(diào)用函數(shù)bencode_str("info",fp)將”info”字符串寫入文件?!甶nfo’后面的內(nèi)容也為一個(gè)dictonary,所以仍舊要用bencode_begin_dict(fp)!=1寫入’d’字符。倒數(shù)第三個(gè)字符’e’便是此dictonary的結(jié)束符。調(diào)用函數(shù)m_btfiles.FillMetaInfo()將儲(chǔ)存在m_btfiles中的數(shù)據(jù)文件信息寫入種子文件。FillMetaInfo()針對(duì)源文件是否為多文件有兩種做法,我們舉的例子是單文件情形,所以此函數(shù)寫入了”length”和”name”兩項(xiàng)。調(diào)用函數(shù)bencode_str("piecelength",fp)和bencode_int(m_piece_length,fp)寫入長(zhǎng)為12的字符串”piecelength”和m_piece_length的數(shù)值:262144,也就是256K。10,調(diào)用bencode_str("pieces",fp)將長(zhǎng)為6的字符串”piece”寫入文件。11,調(diào)用bencode_buf((constchar*)m_hash_table,m_hashtable_length,fp)將m_hash_table寫入文件。注意前3個(gè)數(shù)是380,表明有19個(gè)Hash值長(zhǎng)20的piece。12,兩次調(diào)用bencode_end_dict_list(fp)將dictonary的結(jié)束符’e’寫入文件。13,退出,種子文件制作完成。intbtContent::InitialFromMI(constchar*metainfo_fname,constchar*saveas)此函數(shù)讀取種子文件包含的信息初始化btContent類BTCONTENT。源代碼和注釋如下:intbtContent::InitialFromMI(constchar*metainfo_fname,constchar*saveas){#defineERR_RETURN(){if(b)delete[]b;return-1;}unsignedchar*ptr=m_shake_buffer;char*b;constchar*s;size_tflen,q,r;//將種子文件信息讀入內(nèi)存區(qū)域b中。b=_file2mem(metainfo_fname,&flen);if(!b)return-1;//將announceURL的信息拷貝入s中,長(zhǎng)度為r。if(!meta_str("announce",&s,&r))ERR_RETURN();if(r>MAXPATHLEN)ERR_RETURN();m_announce=newchar[r+1];memcpy(m_announce,s,r);m_announce[r]='\0';//infohashif(!(r=meta_pos("info")))ERR_RETURN();//r現(xiàn)在處于種子文件中”info”字符串后面一個(gè)字節(jié)的位置。if(!(q=decode_dict(b+r,flen-r,(char*)0)))ERR_RETURN();//解析info這個(gè)dictonary。Sha1(b+r,q,m_shake_buffer+28);//將info這個(gè)dictonary的SHA1Hash值(從’d’到’e’)放入m_shake_buffer中。if(meta_int("creationdate",&r))m_create_date=(time_t)r;//將”pieces”字符串后面的數(shù)值(表明了hashtable的長(zhǎng)度。例如此數(shù)值為390,則有19個(gè)piece,hashtable長(zhǎng)19×20=390。)放入m_hashtable_length中。if(!meta_str("info|pieces",&s,&m_hashtable_length)||m_hashtable_length%20!=0)ERR_RETURN();m_hash_table=newunsignedchar[m_hashtable_length];#ifndefWINDOWSif(!m_hash_table)ERR_RETURN();#endif//將種子文件中的哈希表填充入m_hash_table,與tracker服務(wù)器通信時(shí)會(huì)發(fā)送m_hash_table的內(nèi)容以便讓tracker服務(wù)器辨別客戶端是否在使用服務(wù)器端已有的并且正確的種子文件。memcpy(m_hash_table,s,m_hashtable_length);//將種子文件中規(guī)定的piece長(zhǎng)度賦給m_piece_length。if(!meta_int("info|piecelength",&m_piece_length))ERR_RETURN();m_npieces=m_hashtable_length/20;if(m_piece_length>cfg_max_slice_size*(cfg_req_queue_length/2)){fprintf(stderr,"error,piecelengthtoolong(big,OK?:-))[%u].pleaserecompileCTorrentwithalargercfg_req_queue_lengthorcfg_max_slice_sizein<btconfig.h>.\n",m_piece_length);

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論