<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 56,  comments - 12,  trackbacks - 0

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

     

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

    首先創(chuàng)建一個監(jiān)聽 socket ,然后將這個 socket 加入 poll 的事件源;

    隨后進入服務處理循環(huán),即:

     

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

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

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

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

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

     

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

     

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

    1、  將網絡 I/O 和數(shù)據(jù)分析處理分離。

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

     

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

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

     

    RawServer 的成員函數(shù)中,對外暴露的有:

     

    u       __init__ :(初始化函數(shù))

     

    u       add_task()

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

     

    u       bind()

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

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

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

     

    u       start_connection()

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

     

    u       listen_forever()

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

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

     

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

     

    u       handle_events()

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

     

    if sock == self.server.fileno()

     

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

    連接事件的處理:

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

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

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

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

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

     

    if (event & POLLIN) != 0:

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

    讀事件的處理:

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

    然后讀取數(shù)據(jù);

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

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

     

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

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

    寫事件的處理:

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

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

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

     

    u       scan_for_timeouts()

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

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

     

    u       pop_unscheduled()

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

     

     

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

     

     

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

     

    我們再來小結一下。

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

    下一節(jié),我們開始分析具體實現(xiàn) tracker HTTP 協(xié)議處理的 HTTPHandler 類和 Tracker 類。

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

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

    常用鏈接

    留言簿(2)

    隨筆分類(56)

    隨筆檔案(56)

    搜索

    •  

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 深夜特黄a级毛片免费播放| 亚洲成a人片在线观看精品| 一级黄色免费大片| 四虎永久免费地址在线网站| 亚洲日韩精品无码专区加勒比| 在线看片无码永久免费视频| 亚洲综合欧美色五月俺也去| 最近的中文字幕大全免费版| 亚洲一久久久久久久久| 免费看美女让人桶尿口| 久久无码av亚洲精品色午夜| 亚洲AV无码乱码精品国产| 日日摸夜夜添夜夜免费视频| 久久久久亚洲AV无码专区网站 | 亚洲人成色在线观看| 免费无遮挡无码视频网站| 立即播放免费毛片一级| 亚洲欧洲自拍拍偷精品 美利坚 | 日韩高清免费观看| 国产精品内射视频免费| 亚洲国产精品无码一线岛国| **真实毛片免费观看| 国产午夜亚洲精品| 无码国产亚洲日韩国精品视频一区二区三区 | 亚洲首页国产精品丝袜| 国产精品美女自在线观看免费| 午夜在线免费视频 | 国产精品99久久免费观看| 亚洲日韩乱码久久久久久| 成人免费无码大片A毛片抽搐色欲| 国产综合激情在线亚洲第一页| 久久久久亚洲精品无码网址| 在线观看永久免费| 精品免费AV一区二区三区| 国产国拍精品亚洲AV片 | 亚洲成a人无码av波多野按摩| 怡红院免费的全部视频| 亚洲男人的天堂久久精品| 亚洲?V乱码久久精品蜜桃 | 真正全免费视频a毛片| 亚洲第一精品福利|