BT源代碼學習心得(六):跟蹤服務器(Tracker)的代碼分析(初始化)

BT源代碼學習心得(六):跟蹤服務器(Tracker)的代碼分析(初始化)

發信人: wolfenstein (NeverSayNever), 個人文集
標  題: BT源代碼學習心得(六):跟蹤服務器(Tracker)的代碼分析(初始化)
發信站: 水木社區 (Mon Aug  8 11:30:43 2005), 文集
(本文包含HTML標記,終端模式下可能無法正確瀏覽)
    Tracker在BT中是一個很重要的部分。這個名詞我注意到以前的文章中都是直接引用,
沒有翻譯過來,想了一下,決定把它翻譯成跟蹤服務器。
    在BT下載中,種子文件表明了要下載的文件的信息和對它進行檢查的消息摘要碼,但是
每個對等客戶(peer,以後我把peer全部翻譯成對等客戶,以區別client)要獲取其它對等客
戶的信息時,還是要和跟蹤服務器聯繫的。跟蹤服務器上面不保存任何和種子所代表的內容
有關的文件,它只記錄所有下載該種子的機器的IP地址,端口等信息,並在客戶向它請求是
返回一些這樣的信息列表,具體的實際內容,由對等客戶之間完成交互。
    跟蹤服務器的代碼實現在BitTorrent/track.py中,在bttrack.py中只是很簡單得一行

    track(argv[1:])
    這樣就把參數傳到track.py的track函數。track函數本身也比較簡單,處理參數和相關
的配置文件,建立一個RawServer,然後用create_serversocket創建服務器套接字,然後開
始服務。關於在BT中使用網絡服務上次已經有很詳細地介紹,這裏不再重複。只是針對
tracker函數的具體情況,分析一下運行到listen_forever後的情況,首先,建立了
Tracker對象,打開了在某個端口(config['port'])偵聽的網絡服務,這個函數的處理對象
是一個HTTPHandler。所以我們要分析程序的流程只需要先分析Tracker的初始化函數,看看
它創建後都做了些什麼,然後再看HTTPHandler實際分析它的網絡協議。
    在Tracker對象的初始化函數中,首先還是對各種變量的初始化。然後要從一個狀態文
件中進行一些狀態恢復,也就是恢復state變量。這個變量中的值很重要,我們可以需要從
一些地方來得知它的結構,狀態文件的讀取和保存出得不到它的信息,因爲這兩處的實現方
式就是bencode和bdecode,只能保證無論state的結構是什麼都能合適得被保存和恢復,由
此又看出bencode編碼設計的巧妙。但是有一個函數對我們分析state的內部結構很有幫助,
那就是statefiletemplate,這個函數檢查state中的值是否合法,因此我們可以從這裏得到
state的一些結構信息。
    首先,state必須是一個字典類型的變量。然後檢查每一項的值。如果發現一項關鍵字
是'peers',那麼它的值必須也是一個字典,這個字典是一個以種子文件的信息部分的消息
摘要值爲關鍵字的字典,由於sha摘要算法比較好得滿足了摘要算法的要求,即不同的種子
文件它們生成相同摘要的概率極小。而且由於這是由種子文件的內容生成的摘要值,因此即
使把種子文件改名,還是可以識別出來是哪個種子文件。因此'peers'的值可以看成是爲每
一個種子文件記錄的信息,那麼爲每個種子文件記錄的是什麼信息呢?這個信息又是一個字
典,這次以每個對等客戶的ID爲關鍵字,每個對等客戶在連接到跟蹤服務器的時候都會爲自
己生成一個ID,這個ID怎麼生成的以後看客戶端的代碼可以知道,現在我們知道的是,它的
長度必須爲20。這個字典的值,嗯,又是個字典,不過這個字典的意義就明顯多啦,包括了
IP是多少,端口是多少,還剩多少沒有下載完。因此state的內容可以看成是這樣的:{'
peers':{},...},其中peers的結構是這樣的:{hash1:{ID1:{'ip':xxx.xxx.xxx.xxx,'
port':xxxx,left:XXXX},ID2:{'ip':yyy.yyy.yyy.yyy,'port':yyyy,left:YYYY},...},
hash2:{...},...}。以上是state中'peers'這一項。'completed'這一項就相對結構簡單了
,它記錄的是每個種子文件的下載完成情況,它的結構是個字典,以每個種子的信息部分的
消息摘要值爲關鍵字,而對應的值就是一個整數,表示該種子文件已經有多少人完成了下載
。接下來是'allowed'項,這項記錄了該跟蹤服務器所關注的所有的種子的信息,仍然以信
息部分的消息摘要值爲關鍵字,內容就是該種子文件的實際信息,從後面的分析(對
BitTorrent/parsedir.py的分析)可以知道是哪些信息,另外由於之前對種子文件的內部結
構我們已經比較清楚,所以也可以猜出部分。state中還有'allowed_dir_files'項,這一項
也是記錄文件信息的字典,但它是以每個文件的文件名爲關鍵字(而不是消息摘要值),每個
文件的項目是一個列表,結構如下:[(文件修改時間,文件大小),消息摘要值],就是說,這
個以文件名爲關鍵字的字典它的每一個值都是一個列表,這個列表有兩個元素,第一個元素
是一個二元組,內容是文件修改時間和文件大小,第二個元素是消息摘要值。最後,我們注
意到statefiletemplate在處理'allowed'項和'allowed_dir_files'項時還有一些額外的檢
查代碼,即所有在'allowed'項裏面出現的元素,它的消息摘要值都必須在'
allowed_dir_files'項中出現,且'allowed_dir_files'中所有的項中的值的消息摘要部分
必須在'allowed'中出現,另外'allowed_dir_files'中不得出現重複的消息摘要值('
allowed'項本身就以消息摘要值爲關鍵字,而字典的關鍵字已經保證不會重複)。
    因此現在我們知道了state中的注意部分的結構。下面我們注意這兩句:
    self.downloads    = self.state.setdefault('peers', {})
    self.completed    = self.state.setdefault('completed', {})
    這樣就把state中的'peers'和'completed'的值傳到了downloads和completed中,更重
要的是,以後在跟蹤服務器的運行過程中,如果'peers'和'completed'的值發生改變(那簡
直是一定的),state中的相應值也會發生變化,這樣,保存dfile時,就可以及時更新
state的值了。以後我們分析跟蹤服務器運行過程的時候少不了和它們打交道,現在我們可
以先記住,downloads保存了所有的下載的客戶端的信息,completed保存所有的種子的下載
完成情況的統計信息。
    下面的這個for循環根據配置文件處理NAT的問題,以及計算種子的個數。completed只
是記錄所有下載完成的客戶的數目,而只有已經下載完成(left=0),但是還在downloads中
出現(即下載完畢但是沒有關閉客戶端)的客戶端纔算是一個種子。這裏我們可以很容易得看
出,seedcount是一個以信息摘要爲關鍵字,整型爲值的統計種子數的一個字典。
    下面是一個計算的變量,times表示了每個種子(以信息摘要爲關鍵字)中每個客戶(以客
戶ID爲關鍵字)的上次的有活動的時間。接下來增加了兩個任務,每隔一段時間保存一下
dfile,並且檢查下載的客戶端是否已經有很長時間沒有反應的。
    接下來準備一個日誌文件,並試圖把標準輸出重定向到這個日誌文件中。
    最後要去尋找該跟蹤服務器所關注的所有的種子,即parsedir,這個函數可以自己去看
,相信在知道了種子文件的編碼格式和前面的狀態中的項的要求後,不難分析。總得說來,
這個函數做了以下事情,即尋找某個目錄下所有的.torrent文件,把這些文件中的信息讀取
進來,並且排除錯誤,重複等等不合要求的,然後進行加工,輸出符合要求的結果,儲存在
allowed和allowed_dir_files中,進而影響state。
    現在tracker對象已經建立起來,它已經有它要進行跟蹤的所有種子的信息,並且準備
好了維護所有連接進來的客戶的列表,因此它可以正式開始提供跟蹤服務了。下一次我們就
可以看看tracker動起來的效果。
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章