posts - 56,  comments - 12,  trackbacks - 0

          前言:
           自從7月份寫完“客戶端源碼分析之五:Encoder 與 Connection 類”后,我就停止了繼續對BT源碼的分析。原因很多,最主要的還是懶惰吧。臨到歲末,終于下定決心,無論如何,要完成這一系列的文章,對自己也算有個交待。
            前面的幾篇文章,都是深入到源碼的某一部分細節之中,雖然很清晰,但無助于讀者對整體構架的把握(其實我自己當時也比較糊涂)。這一次重新開始讀源碼,重 點順著幾條線索往下讀,感覺原來雜亂無序的代碼突然變得清晰明了起來,呵呵,其實不是代碼雜亂,只是我原來的閱讀思路比較混亂的緣故。
           讀者可以拋開前面幾篇文章,從這一篇開始往下讀,希望能有所收獲。
           源碼分析類的文章,比較難寫,小馬哥畢竟沒有候捷的春秋筆法(甚至連作文都寫不好),能把一個深奧復雜的STL源碼分析的如此透徹,所以只好嘗試一些傻辦法,這些傻辦法包括:
          在源碼上直接加注釋。但我只對重點的部分增加一些注釋,細節的東西就不再深究,這樣有助于寫作的進度。
          用①、②、③這樣的序號來指引代碼的閱讀線索
          用不同顏色標注出代碼中值得注意的地方。
          你有什么好的建議,歡迎提出來。
          好,我們進入正題。

          BT客戶端的 main() 函數:
           C 和c++的可執行程序,通常都有一個 main() 函數,一切從這里開始。而 python 這種解釋性語言,并沒有 main() 函數的概念。你傳遞給解釋器一個 .py 擴展名的python源碼文件,解釋器就會順序去解釋執行這個文件中的代碼。所以,BT客戶端的執行,是從最先被 python 解釋器解釋執行的那個文件開始的。這個文件應該是 btdownloadheadless.py,至于誰又來通知讓 python 解釋器執行這個文件的,我們以后再討論。

          【btdownloadheadless.py】(在BT源碼的根目錄下)
          ②def run(params):
              try:
                  import curses
                  curses.initscr()
                  cols = curses.COLS
                  # endwin() De-initialize the library, and return terminal to normal status
                  curses.endwin()
              
              except:
                  cols = 80
          h = HeadlessDisplayer()
          # 調用 download.py 中的 download 函數,我們的重點將轉移到 download.py 文件,注意,該文件及以后要分析的代碼都在 BT源碼的 BitTorrent 子目錄下。
          ③    download(params, h.chooseFile, h.display, h.finished, h.error, Event(), cols, h.newpath)
              if not h.done:
                  h.failed()

           # 所有的 python的入門書籍中,都會告訴你下面這段代碼的含義。這就是最先被解釋執行的代碼所在。
          ①if __name__ == '__main__':
          run(argv[1:])


          【download.py】
          這個文件中,最主要的就是 download() 函數,客戶端所有的一切都是從這里開始。這個函數代碼比較多,我必須挑出其中最重要的代碼,其它的部分,后續再分析。

          ④rawserver = RawServer(doneflag, config['timeout_check_interval'], config['timeout'], errorfunc
          = errorfunc, maxconnects = config['max_allow_in'])

          # 選擇一個可用的端口作為監聽端口
          for listen_port in xrange(config['minport'], config['maxport'] + 1):
          try:
               rawserver.bind(listen_port, config['bind'])
                  break
          except socketerror, e:
               pass
          else:
          errorfunc("Couldn't listen - " + str(e))
          return

          encoder = Encoder(connecter, rawserver,
          myid, config['max_message_length'], rawserver.add_task,
          config['keepalive_interval'], infohash, config['max_initiate'])

          rawserver.listen_forever(encoder)

          客 戶端的核心類就是 RawServer,這個類我在“服務器端源碼分析”(可在論壇中找到)文章中分析過它的作用,這里不再贅述。通過調用它的 listen_forever() 函數,BT 客戶端就進入了一個循環之中。此后,所有的下載、上傳、文件存儲以及其它工作都在這一次次的循環之中完成。
          注意到,在進入循環之前,調用了 RawServer::bind() 函數,BT客戶端會選擇一個可用的端口,然后通過監聽這個端口,從而可以接受其它 peer 的連接請求(也就是BT對等連接)。這個端口可以稱為“監聽端口”。如果你有網絡服務器的編程經驗,從它的循環處理邏輯、監聽端口的處理可以知道,這顯然 就是一個典型的網絡服務器的表現。所以,我在以前的文章中曾經說過,BT客戶端同時也是一個服務器。
          一旦在監聽端口上有某個peer發來連接請 求,BT客戶端會創建一個新的端口,這個端口用來與請求者建立連接;有幾個peer請求連接,就會創建幾個端口。我們可以說這是一些“被動端口”。在循環 的處理過程中,BT客戶端還會根據情況,向其它 peer主動發出連接請求,每個連接也需要一個端口,這些端口可以稱為“主動端口”。其實,這些都是網絡編程中最基本的概念,不清楚的朋友還是首先要去看 看這方面的書籍。
          這樣,就有了一個“監聽端口”,幾個“被動端口”和幾個“主動端口”,BT客戶端通過 select() 函數來監視這些端口,一旦“監聽端口”或者“被動端口”上有數據到來,或者有數據需要從“主動端口”上發送出去,那么select() 都可以及時感知并進行處理(其實是一個輪詢的過程),這就稱為“I/O多路復用”。如果“被動端口”上有數據到來,那么這是BT對等連接的協議數據,需要 按照BT對等協議進行分析,并根據分析結果進行相應處理,這個分析處理的工作就是由 Encoder 類來完成的。所以在 listen_forever() 函數中傳遞的參數就是一個 Encoder 類對象。關于對 Encoder 類的分析,以后分析到BT對等協議的處理的時候再說。

          小結:
           通過這篇文章,我們了解了BT客戶端是從哪里執行的,相應的源碼在哪 里。同時我們知道了BT客戶端是以一個服務器循環的形式運行的,它需要監聽一個端口,用于接受其它peers的連接請求;在循環過程中,它通過 select 來實現“I/O多路復用”;并由 Encoder 類來完成BT對等協議的分析處理。

          posted on 2007-01-19 00:22 苦笑枯 閱讀(787) 評論(0)  編輯  收藏 所屬分類: P2P
          收藏來自互聯網,僅供學習。若有侵權,請與我聯系!

          <2007年1月>
          31123456
          78910111213
          14151617181920
          21222324252627
          28293031123
          45678910

          常用鏈接

          留言簿(2)

          隨筆分類(56)

          隨筆檔案(56)

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 威信县| 昌江| 秭归县| 德保县| 宕昌县| 黄平县| 濮阳市| 东乡| 本溪市| 嵩明县| 永丰县| 乐安县| 西安市| 绥德县| 二手房| 东丽区| 河津市| 屯留县| 正定县| 达拉特旗| 唐山市| 宜川县| 云浮市| 仙居县| 翼城县| 汶川县| 云和县| 富宁县| 明水县| 潼关县| 嘉兴市| 临泽县| 承德县| 玉林市| 大港区| 营口市| 天台县| 诸城市| 鄢陵县| 龙山县| 福海县|