1. 程式人生 > >不同區域網間的P2P通訊技術

不同區域網間的P2P通訊技術

    NAT(Net Address Translation):網路地址轉換,即區域網通常通過一個具有公網IP的代理閘道器伺服器連到internet共享上網。局域往內的機器並不具有公網IP地址,只有內網IP地址,若要和internet上的HTTP伺服器通訊,代理閘道器便會建立一個埠來和這個網內機器通訊,並通過該埠和HTTP伺服器交換資料。最終,網內機器-->代理閘道器-->HTTP伺服器,在一個會話期間,各自的埠保持了對映關係,特別是代理閘道器和網內機器的埠對映,使得代理閘道器不會把接收到的資料包發錯物件。區域網內的機器在閘道器處,就是靠NAT來對映埠實現internet連線。因此,NAT也稱為埠對映。埠對映之後,在一個會話期間保持,對於TCP連線是直到連線斷開才銷燬,而對於UDP,卻存在一個不定的生存期。
    如果兩臺機器A和B,分別處於兩個區域網內,要通過internet通訊,即為P2P連線通訊。目前的internet使用IPV4,採用32位IP地址,主要被用來進行C/S形式的通訊,需要共享的資源集中放於internet伺服器上,IPV4對於P2P分散式資源共享的支援,極不友好。首先,32位IP地址已經不敷使用,公網IP地址日趨緊張,只能使用區域網共享公網IP的方式,區域網正是為了臨時應對IP耗盡而出現的,長遠的解決辦法是研究IPV6。其次,分別處於兩個區域網內的機器要通訊,由於對方沒有公網IP,直接呼叫對方是不可能的,必須

藉助第三方中介間接地通訊,解決方法有如下幾種:
1、實現區域網內的資料鏈路層協議,就是設計一個類時TCP/IP的協議,由它來代替TCP/IP協議,由它直接基於網絡卡硬體獲取資料。這是十分複雜的。
2、依靠internet上的公網伺服器中轉資料,但對於大資料量的中轉,顯然受到伺服器和網路的負載極限的限制。
3、依靠internet上的公網伺服器做媒介,將這兩臺分別處於不同區域網的機器相互通知給對方,在它們建立連線之後,伺服器即脫離關係。這種方式下,伺服器把A的NAT埠對映關係告訴B,又把B的NAT埠對映關係告訴A,這樣AB相互知道對方的埠對映關係之後,就能建立連線。因為A和B各自的埠對映關係是靠各自的代理閘道器動態建立的,動態建立的對映埠不得告知對方。
4、上面的第三種辦法,也可以採用靜態埠對映方式,這樣就無需中介伺服器對A和B做介紹。在各方自的代理閘道器上,可以在代理工具裡將某個埠(如1002)和區域網內的某臺機器(如內網IP為192.168.18.23,埠1003)做好靜態對映,這樣,代理閘道器會自動地將出入於1002埠的資料發往192.168.18.23的1003埠。當然,通訊之前,必須對對方的埠對映關係做配置。有多少臺網內機器要通訊,就得對映多少個不同的埠,同時在另一個區域網內的機器就要做多少個配置。在區域網內搭建HTTP、FTP等伺服器就是通過靜態對映埠來實現的,這個埠一般不是HTTP、FTP的預設80和23,所以對這類站點的訪問往往會在URL里加上埠號。

   由此所知,上述前兩種辦法在簡單應用中是不可取的,只有後兩種可行。它們又各有缺點,第三種動態對映埠,需要增加中介伺服器,第四種靜態對映埠,在需要通訊的各方機器很多的情況下,做手工埠對映和配置都是很繁瑣的,並且一方新增一臺機器,就需要在其餘對方增加配置。
採用動態和靜態相結合的辦法是可以推想的,然而其可行性還必須經過測試。可以這樣設計,為了讓所有通訊機器彼此知曉並定位。我們可以在局域網裡,只對一臺機器在代理閘道器處做靜態埠對映,本區域網內的機器都向它登記。而兩個區域網各自只做一項對對方的對映配置。兩個區域網之間,沒有靜態對映埠的機器要通訊,就靠有對映的機器來擔當“介紹”。

    就區域網和NAT的問題實際上還很多,比如各自的區域網的結構不同,局域網裡可能又有子區域網,區域網可能是NAT代理結構,但也可能是HTTP代理,Sock4、Sock5代理等結構,NAT又分嚴格的和非嚴格NAT,嚴格NAT限制很多,更不便於P2P。不過,軟體不能實現的地方,可以考慮改變硬體結構,例如將嚴格NAT變為非嚴格NAT。如果硬體改變不得,那麼Internet整體上就有10%的系統不能實現P2P,除非等到正處於研發的IPv6協議出來。P2P要解決的唯一技術難題是如何發現、定位和定址對方,就是如何穿透NAT、HTTP、Sock等代理和如何穿透防火牆找到對方並建立起通訊的問題。由於絕大多數區域網是NAT代理結構,所以前面對NAT論述比較詳細,也是網上討論最多的話題,相比之下,穿透Http、Sock代理就簡單一些。此外,穿透NAT發現對等點的辦法還有一些,例如多播,但由於現有Internet對多播並不友好,同時多播是無連線和不可靠的,其實現有難度。許多軟體都是按照上述一些技術實現了P2P通訊,著名的有MSN、QQ和BitTorrent下載軟體等。

    我們不希望在IP層實現我們的P2P,而是希望在應用層,利用Windows提供的Socket建立P2P,至多下到用原始Raw Socket來寫P2P。首先看,我們對於公網有伺服器做“中介(非中轉)”的P2P怎麼實現。

原理講述:
  例如AB兩臺機器分別處於兩個不同的區域網後,由Server做中介,先看連線過程。
  A首先連線伺服器,採用UDP發包給Server,這個包包括了A的使用者資訊,類似於QQ的QQ號、呢稱等。Server方,可以用CSocket::GetPeerName()得到A的IP及埠,但得到的IP和埠應該是A的代理閘道器的公網PublicIP及其對映埠NatPort,該對映埠就是A的代理閘道器為A的本次UDP通訊臨時分配的Nat埠。可以斷言,得到的埠一定不是A的內網IP和內網UDP埠。
  伺服器然後將A的公網IP、對映埠、使用者資訊等儲存到(記憶體列表或者資料庫),這樣標誌著A已經上線。伺服器馬上將其它線上的使用者資訊發回給A,包括其它使用者的代理閘道器的公網IP及Nat埠。A同樣將線上使用者的這些資訊儲存並顯示為列表,期待A使用者做出選擇。
  對於B,同樣有上述的上線過程。
  當A使用者做出選擇,要和線上的B使用者通訊時,A首先發UDP包給B的公網IP及Nat埠,並立即發一個UDP包給伺服器,讓伺服器去通知B,叫B給A也發一個UDP包。換句話說,1、A發包給PublicB, 2、A發包給Server,3、Server發包給PublicB,4、B發包給PublicA。
  上面的敘述用到了"Public"字樣,它代表代理閘道器的公網IP及其對映埠。  
  由於A和B各自的閘道器都儲存了各自的埠對映關係,發到閘道器的資料,閘道器會按照這個對映關係轉發給A和B。當A和B都分別收到對方發來的UDP包以後,連線宣告成功,伺服器即可以脫離,AB即可以用UDP通訊。

  何以如此麻煩?

  A在發UDP包給Server上線時,A的閘道器(A.Gate)就分配一個Nat埠(A.NatPort)給A,用於A和Server間的本次UDP會話,但A的閘道器明確標記,這個Nat埠,僅能用於A和Server之間的UDP通訊,不能挪著它用。並且,這個臨時分配的埠,只能保持一個很短的時效,也許是一兩秒吧。這個時間內,如果A與Server沒有任何通訊,那麼這個對映埠就宣告無效。下次,A和Server又要通訊時,A的閘道器又會重新分配一個新的埠。這段表明三點:
  1、A與Server的通訊,需要A閘道器分配Nat埠來中轉。
  2、Nat埠只能用於A和Server間的通訊。
  3、Nat埠存在生存期,長時間A和Server無通訊,該埠即宣告無效。
  就是這些麻煩,使得我們的連線過程必須繞很多彎。
  A和B的通訊,就是藉助事先AB分別與Server連線時,在各自的閘道器處建立的埠對映來通訊。為避免上面的2、3點麻煩,A和B在初次連線時,必須幾乎同時向對方發包。
  如果A、B不同時發包給對方,它們各自的閘道器就會慮掉對方的包,因為該包不是Server發來的包,叫做不請自來的包。
  並且,即便AB各自的閘道器不慮掉非Server發來的包,它們各自的Nat埠也有一個時效。那麼A與Server,B與Server就不得不發心跳包,以維持各自的對映埠,保證其不失效。
  上面的過程中,如果A和B建立連線失敗,可以迴圈這個過程,直到一個有限的次數之後,仍不能連線則宣告失敗。