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