BT客戶端源碼分析之一:總述
作者:小馬哥
日期:2004-6-24
概述:
相對(duì)于 tracker 服務(wù)器來說,BT客戶端要復(fù)雜的多,Bram Cohen 花了一年 full time 的時(shí)間來完成 BT,我估計(jì)其中大部分時(shí)間是用在 BT 客戶端的實(shí)現(xiàn)和調(diào)試上了。
由
于 BT 客戶端涉及的代碼比較多,我不能再象分析 tracker
服務(wù)器那樣,走上來就深入到細(xì)節(jié)之中去,那樣的話,我寫的暈暈糊糊,大家看起來也不知所云。所以第一篇文章先來談?wù)効蛻舳说墓δ堋⑾嚓P(guān)協(xié)議,以及客戶端的
總體架構(gòu)和相關(guān)的類的層次結(jié)構(gòu)。這樣,從整體上把握之后,大家在自己分析代碼的過程中,就能做到胸有成竹。
客戶端的功能:
不看代碼,只根據(jù) BT 的相關(guān)原理,大致可以推測(cè),客戶端需要完成以下功能:
1、解析 torrent 文件,獲取要下載的文件的詳細(xì)信息,并在磁盤上創(chuàng)建空文件。
2、與 tracker服務(wù)器 建立連接,并交互消息。
3、根據(jù)從 tracker 得到的信息,跟其它 peers 建立連接,并下載需要的文件片斷
4、監(jiān)聽某端口,等待其它peers 的連接,并提供文件片斷的上傳。
相關(guān)協(xié)議:
對(duì)客戶端來說,它需要處理兩種協(xié)議:
1、與 tracker 服務(wù)器交互的 track HTTP協(xié)議。
2、與其它 peers 交互的 BT 對(duì)等協(xié)議。
總體架構(gòu):
從總體上來看,BT客戶端實(shí)際是以一個(gè)服務(wù)器的形式在運(yùn)行。這一點(diǎn)似乎有些難以理解,但確實(shí)是這樣。
為什么是一個(gè)服務(wù)器了?
客
戶端的主要功能是下載文件,但作為一種P2P軟件,同時(shí)它必須提供上傳服務(wù),也就是它必須守候在某一個(gè)端口上,等待其它peers
的連接請(qǐng)求。從這一點(diǎn)上來說,它必須以一個(gè)服務(wù)器的形式運(yùn)行。我們?cè)诤竺鎸?shí)際分析代碼的時(shí)候,可以看到,客戶端復(fù)用了 RawServer
類用來實(shí)現(xiàn)網(wǎng)絡(luò)服務(wù)器。
客戶端的代碼,是從 download.py
開始的,首先完成功能1,之后就進(jìn)入服務(wù)器循環(huán),在每一次循環(huán)過程中,完成功能 2、3、4。其中,Rerequester 類負(fù)責(zé)完成功能2,它通過
RawServer::add_task(),向 RawServer 添加自己的任務(wù)函數(shù),這個(gè)任務(wù)函數(shù),每隔一段時(shí)間與 tracker
服務(wù)器進(jìn)行通信。而Encoder、Connecter 等多個(gè)類組合在一起,完成功能3和4。
類層次結(jié)構(gòu):
BT 客戶端涉及的類比較多,我首先大致描述一下這些類的功能,然后給出它們的一個(gè)層次結(jié)構(gòu)。
1、RawServer:負(fù)責(zé)實(shí)現(xiàn)網(wǎng)絡(luò)服務(wù)器
2、Rerequester:負(fù)責(zé)和 tracker 通信。它調(diào)用 RawServer::add_task() ,向 RawServer 添加自己的任務(wù)函數(shù) Rerequester::c()。
3、Encoder:一種 Handler類(在分析 tracker 服務(wù)器時(shí)候提到),負(fù)責(zé)處理與其它peers建立連接和以及對(duì)讀取的數(shù)據(jù)按照BT對(duì)等協(xié)議進(jìn)行分析。
Encoder
類在Encrypter.py中,該文件中,還有一個(gè) Connection 類,而在 Connecter.py 文件中,也有一個(gè)
Connection 類,這兩個(gè)同名的 Connection 類有些蹊蹺,為了區(qū)分,我把它們重新命名為 E-Connection 和
C-Connection。
3.1、E-Connection:負(fù)責(zé) TCP 層次上的連接工作
這兩個(gè)
Connection 是有區(qū)別的,這是因?yàn)锽T對(duì)等協(xié)議需要在兩個(gè)層次上建立連接。首先是 TCP 層次上的連接,也就是經(jīng)過 TCP
的三次握手之后,建立連接,這個(gè)連接由 E-Connection 來管理。在 Encoder::
external_connection_made() 函數(shù)中可以看到,一旦有外部連接到來,則創(chuàng)建一個(gè) E-Connection 類。
3.2、C-Connection:管理對(duì)等協(xié)議層次上的連接。
在 TCP 連接之上,是 BT對(duì)等協(xié)議的連接,它需要經(jīng)過BT對(duì)等協(xié)議的兩次“握手”,握手的細(xì)節(jié)大家去看BT對(duì)等協(xié)議。過程是這樣的:
為了便于述說,我們假設(shè)一個(gè)BT客戶端為 A,另一個(gè)客戶端為 X。
如
果是X主動(dòng)向A發(fā)起連接,那么在TCP連接建立之后,A立刻利用這個(gè)連接向X發(fā)送BT對(duì)等協(xié)議的“握手”消息。同樣,X在連接一旦建立之后,向
A發(fā)送BT對(duì)等協(xié)議的“握手”消息。A一旦接收到X的“握手”消息,那么它就認(rèn)為“握手”成功,建立了BT對(duì)等協(xié)議層次上的連接。我把它叫做“對(duì)等連
接”。A 發(fā)送了一個(gè)消息,同時(shí)接收了一個(gè)消息,所以這個(gè)握手過程是兩次“握手”。
同樣,對(duì)X 來說,因?yàn)檫B接是它主動(dòng)發(fā)起的,所以它在發(fā)送完“握手”消息之后,就等待A的“握手”消息,如果收到,那么它也認(rèn)為“對(duì)等連接”建立了。
一旦“對(duì)等連接”建立之后,雙方就可以通過這個(gè)連接傳遞消息了。
這樣,原來我所疑惑的一個(gè)問題也就有了答案。就是:如果 X 需要從 A 這里下載數(shù)據(jù),那么它會(huì)同 A 建立一個(gè)連接。假如 A 又希望從 X 那里下載數(shù)據(jù),它需不需要重新向 X 發(fā)起另外一個(gè)連接了?答案顯然是不用,它會(huì)利用已有的一條連接。
也就是說,不管是X主動(dòng)向A發(fā)起的連接,還是 A 主動(dòng)向 X發(fā)起的連接,一旦建立之后,它們的效果是一樣的。這個(gè)同我們平時(shí)做 C/S結(jié)構(gòu)的網(wǎng)絡(luò)開發(fā)是有區(qū)別的。
我們可以看到在 E-Connection的初始化函數(shù)中,會(huì)主動(dòng)連接的另一方發(fā)送“握手”消息,在 E-Connection::data_came_in() 中,會(huì)首先對(duì)對(duì)方的“握手”消息進(jìn)行處理。這正是我上面所描述的情形。
在 E-Connection::read_peer_id() 中,是對(duì)“握手”消息的最后一項(xiàng) peer id進(jìn)行處理,一旦正確無誤,那么就認(rèn)為“對(duì)等連接”完成,
self.encoder.connecter.connection_made(self)
在 Connecter::connection_made() 函數(shù)中,就創(chuàng)建了管理“對(duì)等連接”的 C-Connectinon類。所以,更高一層的“對(duì)等連接”是由 C-Connection 來管理的。
3.3、Connecter:連接器,管理下載、上傳、阻塞、片斷選擇、讀寫磁盤等等。
下載和上傳不是孤立的,它們之間相互影響。下載需要有片斷選擇算法,上傳的時(shí)候要考慮阻塞,片斷下載之后,要寫到磁盤上。上傳的時(shí)候,也需要從磁盤讀取。
這些任務(wù),是由 Connecter 來統(tǒng)一調(diào)度的。
類層次結(jié)構(gòu),我用縮進(jìn)來表示一種包含關(guān)系。
Encoder:
E-Connection
C-Connection
Upload
SingleDownloader
Connecter
Choker:負(fù)責(zé)阻塞的管理
Downloader:
SingleDownloader
Picker:片斷選擇策略
StorageWrapper:
先寫這些吧,有什么我再補(bǔ)充進(jìn)來。
posted on 2007-01-19 00:19
苦笑枯 閱讀(443)
評(píng)論(0) 編輯 收藏 所屬分類:
P2P