tracker
服務器是
BT
下載中必須的角色。一個
BT client
在下載開始以及下載進行的過程中,要不停的與
tracker
服務器進行通信,以報告自己的信息,并獲取其它下載
client
的信息。這種通信是通過
HTTP
協議進行的,又被稱為
tracker HTTP
協議,它的過程是這樣的:
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
這么簡單方便。
posted on 2007-01-19 00:17
苦笑枯 閱讀(362)
評論(0) 編輯 收藏 所屬分類:
P2P