client 向 tracker 發(fā)一個(gè) HTTP 的 GET 請(qǐng)求,并把它自己的信息放在 GET 的參數(shù)中;這個(gè)請(qǐng)求的大致意思是:我是 xxx (一個(gè)唯一的 id ),我想下載 yyy 文件,我的 ip 是 aaa ,我用的端口是 bbb 。。。
tracker 對(duì)所有下載者的信息進(jìn)行維護(hù),當(dāng)它收到一個(gè)請(qǐng)求后,首先把對(duì)方的信息記錄下來(如果已經(jīng)記錄在案,那么就檢查是否需要更新),然后將一部分(并非全部,根據(jù)設(shè)置的參數(shù)已經(jīng)下載者的請(qǐng)求)參與下載同一個(gè)文件(一個(gè) tracker 服務(wù)器可能同時(shí)維護(hù)多個(gè)文件的下載)的下載者的信息返回給對(duì)方。
Client 在收到 tracker 的響應(yīng)后,就能獲取其它下載者的信息,那么它就可以根據(jù)這些信息,與其它下載者建立連接,從它們那里下載文件片斷。
關(guān)于 client 和 tracker 之間通信協(xié)議的細(xì)節(jié),在“ BT 協(xié)議規(guī)范”中已經(jīng)給出,這里不再重復(fù)。下面我們具體分析 tracker 服務(wù)器的實(shí)現(xiàn)細(xì)節(jié)。
從哪里開始?
要建立一個(gè) tracker 服務(wù)器,只要運(yùn)行 bttrack.py 程序就行了,它最少需要一個(gè)參數(shù),就是 –dfile ,這個(gè)參數(shù)指定了保存下載信息的文件。 Bttrack.py 調(diào)用 track.py 中的 track() 函數(shù)。因此,我們跟蹤到 track.py 中去看 track() 函數(shù)。
Track.py : track()
這個(gè)函數(shù)首先對(duì)命令行的參數(shù)進(jìn)行檢查;然后將這些參數(shù)保存到 config 字典中。在 BT 中所有的工具程序,都有類似的處理方式。
接下來的代碼:
r = RawServer(Event(), config['timeout_check_interval'], config['socket_timeout'])
t = Tracker(config, r)
r.bind(config['port'], config['bind'], True)
r.listen_forever(HTTPHandler(t.get, config['min_time_between_log_flushes']))
t.save_dfile()
首先是創(chuàng)建一個(gè) RawServer 對(duì)象,這是一個(gè)服務(wù)器對(duì)象,它將實(shí)現(xiàn)一個(gè)網(wǎng)絡(luò)服務(wù)器的一些細(xì)節(jié)封裝起來。不僅 tracker 服務(wù)器用到了 RawServer ,我們以后還可以看到,由于每個(gè) client 端也需要給其它 client 提供下載服務(wù),因此也同時(shí)是一個(gè)服務(wù)器, client 的實(shí)現(xiàn)中,也用到了 RawServer ,這樣, RawServer 的代碼得到了重用。關(guān)于 RawServer 的詳細(xì)實(shí)現(xiàn),在后面的小節(jié)中進(jìn)行分析。
接著是創(chuàng)建一個(gè) Tracker 對(duì)象。
然后讓 RawServer 綁定在指定的端口上(通過命令行傳遞進(jìn)來)。
最后,調(diào)用 RawServer::listen_forever() 函數(shù),使得服務(wù)器投入運(yùn)行。
最后,在服務(wù)器因某些原因結(jié)束運(yùn)行以后,調(diào)用 Tracker::save_dfile() 保存下載信息。這樣,一旦服務(wù)器再次投入運(yùn)行,可以恢復(fù)當(dāng)前的狀態(tài)。
其它信息:
1、 BT 源碼的分布:
把 BT 的源碼展開之后,可以看到有一些 python 程序,還有一些說明文件等等,此外還有一個(gè) BitTorrent 目錄。這些 python 程序,實(shí)際是一些小工具,比如制作 file 的 btmakefile.py 、運(yùn)行 tracker 服務(wù)器的 bttrack.py 、運(yùn)行 BT client 端的 btdownloadheadless.py 等等。而這些程序中,用到的一些 python 類的實(shí)現(xiàn),都放在子目錄 BitTorrent 下面。我們的分析工作,通常是從工具程序入手,比如 bttrack.py ,而隨著分析的展開,則重點(diǎn)是看 BitTorrenet 子目錄下的代碼。
BT 作者 Bram Cohen 在談到如何開發(fā)可維護(hù)的代碼的一篇文章中( http://www.advogato.org/article/258.html ),其中提到的一條就是開發(fā)一些小工具以簡(jiǎn)化工作,我想 BT 的這種源碼結(jié)構(gòu),也正是作者思想的一種體現(xiàn)吧。
2、 我們看到, python 和我們以前接觸的 c/c++ 不一樣的第一個(gè)地方就是它的函數(shù)在定義的時(shí)候,不用指定參數(shù)類型。既然這樣,那么,在調(diào)用函數(shù)的時(shí)候,你可以傳遞任意類型的參數(shù)進(jìn)來。例如這樣的函數(shù):
def foo(arg):
print type(arg)
你可以這樣來調(diào)用:
a = 100
b = “hello world”
foo(a)
foo(b)
輸出結(jié)果是:
<type ‘int’>
<type ‘str’>
這是因?yàn)椋谝淮握{(diào)用 foo() 的時(shí)候,傳遞的是一個(gè)整數(shù)類型,而第二次調(diào)用的時(shí)候,傳遞的是一個(gè)字符串類型。
這種參數(shù)具有動(dòng)態(tài)類型的特性,是 c/c++ 等傳統(tǒng)的語言是所不具備的。這也是 python 被稱為動(dòng)態(tài)語言的一個(gè)原因吧。 C++ 的高級(jí)特性模板,雖然也使得參數(shù)類型可以動(dòng)態(tài)化,但使用起來,遠(yuǎn)沒有 python 這么簡(jiǎn)單方便。