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