posts - 56,  comments - 12,  trackbacks - 0

          這篇文章,我們來分析 RawServer 以及一些相關的類。 RawServer 類的實現代碼,在 BitTorrent 子目錄的 RawServer.py

           

          RawServer 這個類的作用是實現一個網絡服務器。關于網絡編程的知識,《 unix 網絡編程:卷 1 》是最經典的書籍,你如果對這塊不了解,建議抽時間看看這本書。 RawServer 實現的是一種事件多路復用、非阻塞的網絡模型。它使用的是 poll() (而不是我們常見的 select() ,關于 poll select 的比較,也在《 unix 網絡編程:卷 1 》中有介紹)函數,處理過程大致是這樣的:

          首先創建一個監聽 socket ,然后將這個 socket 加入 poll 的事件源;

          隨后進入服務處理循環,即:

           

          調用 poll() 函數,這個函數會阻塞,直到網絡上有某些事件發生或者超時才返回給調用者;

          poll() 返回之后,先檢查一下是否有沒有處理的任務,如果有,那么先完成這些任務。然后根據事件類型進行處理。

          如果是連接請求(監聽 socket 上的 POLLIN 事件)到來,它 accept 這個請求,如果 accept 成功,那么就和一個 client 建立了連接,于是將 accept() 新創建的 socket 加入 poll 的事件源;

          如果在已經建立的連接上(連接 socket 上的 POLLIN 事件),有數據可讀,那么將數據從 client 端讀過來,做進一步處理;

          如果已經建立的連接已經準備好(連接 socket 上的 POLLOUT 事件),可以發送數據,則檢查是否有數據需要發送,如果有,那么發送數據給 client 端。

           

          (所以, tracker 是一個單進程的服務器,并沒有用到線程。)

           

          Bram Cohen 認為軟件的可維護性非常重要,使代碼易于維護的重要一條就是設計可重用的類, RawServer 在設計的時候,充分考慮到了可重用性,集中表現在兩個地方:

          1、  將網絡 I/O 和數據分析處理分離。

          網絡服務器的事件多路復用、網絡 I/O 部分通常是固定不變的,而數據在讀取之后,進行分析處理的過程則是可變的。 RawServer 將可變的數據處理工作,交給另外一個抽象的類 Handler (實際上并沒有這么一個類)來處理。比如,在 tracker 服務器的實現中,具體使用的就是 HTTPHandler 類,而在 以后將要分析的 BT client 實現代碼中,用到的具體的 Handler Encoder 類。

           

          2、  采用任務隊列來抽象出任務處理的過程。

          RawServer 維護了一個任務隊列 unscheduled_tasks (實際是一個二元組的 list ,二元組的第一項是一個函數,第二項是超時時間)。在初始化的時候,首先向這個隊列中加入一個任務: scan_for_timeouts() ,這樣,每隔一段時間,服務器就會去檢查一下是否有連接超時。如果有其它

           

          RawServer 的成員函數中,對外暴露的有:

           

          u       __init__ :(初始化函數)

           

          u       add_task()

                 在任務列表中增加一項任務(一個任務是一個函數以及一個指定的超時時間的組合)

           

          u       bind()

                 首先創建一個 socket ,然后設置 socket 的屬性: SO_REUSEADDR IP_TOS, ,這兩個屬性的具體含義請參考《 unix 網絡編程:卷 1 》,另外還將 socket 設置為非阻塞的。相對于阻塞的 socket 來說,非阻塞的 socket 在網絡 I/O 性能上要提高許多,但是與此同時,編程的復雜度也要提高一些。象 tracker 這種可能同時要處理成千上萬個并發連接的服務器,只能采用非阻塞的 socket 。

                 然后將該 socket 和指定 ip 已經端口綁定;

                 最后把這個 socket 加入 poll 的事件源。

           

          u       start_connection()

                 對外主動建立一個連接,這個函數在處理 NAT 穿越的時候用到了,我們后面分析到 NAT 穿越的時候,再具體講解。

           

          u       listen_forever()

                 這個函數的功能就是實現了我在前面描述的網絡服務器的處理過程。我們看到,它唯一的參數是 handler , handler 的作用就是封裝了對數據的具體處理。

          listen_forever() 把對網絡事件的處理過程,交給了 handle_events() 。

           

          其它函數,包括 handle_events() ,都是內部函數(也就是外部不會直接來調用這些函數)。 Python 沒有 c++ 那樣 public 、 protected private 這樣的保護機制, python 類的內部函數命名的慣例是以下劃線開始,例如 RawServer 中的 _close_dead() 等。

           

          u       handle_events()

          事件處理過程,主要是根據三種不同的網絡事件分別處理,一是連接事件,二是讀事件、三是寫事件。

           

          if sock == self.server.fileno()

           

          這段代碼判斷發生事件的 socket 是否是監聽 socket ,如果是,那么說明是連接事件。

          連接事件的處理:

          通過 accept 來接受連接,并將新建立的 socket 設置為非阻塞。

          判斷當前連接數是否已經達到了最大值(為了限制并發連接的數目,在初始化 RawServer 的時候,需要指定最大連接數目),如果已經達到最大值,那么關閉這個新建的連接。

          否則,根據新的 socket 創建一個 SingleSocket 對象,( SingleSocket 封裝了對 socket 的操作。)將這個對象加入內部的列表 single_sockets 中,以備后用。

          將這個新 socket 加入 poll 的事件源

          最后,調用 Handler external_connection_made() 函數,關于這個函數,在后面分析 HTTPHandler 時再討論。

           

          if (event & POLLIN) != 0:

          這段代碼判斷是否是讀事件

          讀事件的處理:

          首先刷新一下連接的最后更新時間 last_hit )。

          然后讀取數據;

          如果什么也沒讀到,那么說明連接被關閉了(在網絡編程中,如果一個連接正常的被關閉,那么,也會觸發讀事件,只不過什么也讀不到)

          否則,調用 Handler data_came_in() 函數來處理讀到的數據。

           

          if (event & POLLOUT) != 0 and s.socket is not None and not s.is_flushed():

          這段代碼判斷是否是寫事件,而且確實有數據需要發送。在一個連接可以寫的時候,就會發生寫事件。

          寫事件的處理:

          實際代碼是在 SingleSocket try_write() 函數中。

          在一個非阻塞的連接上發送指定大小的數據,很可能在一次發送過程中,數據沒有被完全發送出去(只發送了一部分)就返回了,所以,每次 write 之后,必須判斷是否完全發送了數據。如果沒有發送完,那么下次有讀事件的時候,還得回來繼續發送未完得數據。這也是這個函數叫做 try_write 的原因吧。

          try_write() 在最后,要重新設置 poll 的事件源。如果數據全部發送完畢了,那么只需要監聽讀事件( POLLIN )否則,既要監聽讀事件,也要監聽寫事件( POLLOUT ),這樣,一旦連接變的可寫,可以繼續將剩下的數據發送出去。

           

          u       scan_for_timeouts()

          任務處理函數,它首先把自身加入未處理任務隊列中,這樣,經過一段時間,可以保證這個函數再次被調用,從而達到周期性調用的效果。

          它檢查每個連接是否超過指定時間沒有被刷新,如果是,則該連接可能已經僵死,那么它關閉這個連接。

           

          u       pop_unscheduled()

          從任務列表中彈出一個未處理的任務。

           

           

          RawServer 配合使用的是 SingleSocket 類,這是一個輔助類,主要目的是封裝對 socket 的處理吧。包括數據的發送,都交給它來處理了。這個類比較簡單,大家可以自己去看,我就不羅嗦了。

           

           

          以上是對 RasServer 的具體實現的一個分析,可能讀者看的還是暈暈糊糊,沒辦法,還是必須自己去看源代碼,然后在遇到問題的時候,回頭再來看這篇文章,才會有幫助。如果不親自看源碼,終究是紙上談兵。

           

          我們再來小結一下。

          RawServer 封裝了網絡服務器的實現細節,它實現了一種事件多路處理、非阻塞的網絡模型。它主要負責建立新的連接,從網絡讀取和發送數據,而對讀到的數據的具體處理工作,交給 Handler 類來處理,從而把網絡 I/O 和數據處理分離開來,使得 RawServer 可以重用。 Handler 類是在調用 listen_forever() 的時候,由調用者傳遞進來的,具體到 tracker 服務器,就是 HTTPHandler 。有了 RawServer , tracker 就可以作為一個網絡服務器運行了。

          下一節,我們開始分析具體實現 tracker HTTP 協議處理的 HTTPHandler 類和 Tracker 類。

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

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

          常用鏈接

          留言簿(2)

          隨筆分類(56)

          隨筆檔案(56)

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 黎川县| 大方县| 长海县| 德化县| 阿拉尔市| 大厂| 婺源县| 昭平县| 柏乡县| 宜宾市| 安泽县| 杨浦区| 正阳县| 富源县| 新闻| 迁安市| 安泽县| 临泉县| 阜阳市| 长宁县| 道孚县| 全南县| 平阳县| 穆棱市| 融水| 治多县| 巩义市| 舞钢市| 红原县| 穆棱市| 定边县| 淮安市| 温泉县| 蕲春县| 广河县| 法库县| 苍溪县| 永丰县| 南川市| 银川市| 石城县|