講P2P基礎知識的文章

 

以下將要用到一個叫做NAT的重要名詞,先做點解釋。
NAT是Net Address Translation(網絡地址轉換)的簡稱,就是說,局域網通常靠一個具有公網IP的代理網關服務器連到Internet共享上網。局域網內的機器並不具備公網IP地址,它只有內網地址,假設它要和Internet上的HTTP服務器通信,代理網關便會新建一個端口來和這個網內機器關聯,並通過這個端口來和HTTP服務器交換數據。最終,網內機器->代理網關->HTTP服務器,在一個會話期間,各自的端口保持了映射關係,特別是代理網關和網內機器的端口映射,使得代理網關不會把接收到的數據向網內轉發時,發錯了機器。
局域網內的機器在網關處,就是靠NAT來映射端口並實現Internet連接,因此,NAT也直接被稱爲“端口映射”。端口映射之後,在一個會話期間保持,對於TCP連接是直到連接斷開才銷燬,而對於UDP,卻存在一個不定的生存期,例如2秒。

如果兩臺機器A和B,分別處於兩個局域網內,它們要通過Internet通信,這就是P2P(點到點)連接通信。
目前的Internet使用IPv4協議,採用32位IP地址,主要被用來進行C/S形式的通信,需要共享的資源集中放於Internet服務器上。IPv4對於P2P分佈式資源共享的支持,極不友好。首先,32位IP地址已經不敷使用,公網IP地址日趨緊張,只能使用局域網共享公網IP的方式,局域網正是爲了臨時應對IP耗盡而出現的,長遠的解決辦法是研究IPv6。其次,分別處於兩個局域網內的機器要通信,由於對方沒有公網IP,直接呼叫對方是不可能的,必須藉助第三方“中介”(機器或者軟件)間接地連通,解決辦法下列幾種:
第一:實現局域網內的數據鏈路層協議,就是寫一個類似於TCP/IP的協議,由它來代替Windows系統裏的TCP/IP協議,由它直接基於網卡硬件獲取數據。這是十分複雜的。
第二:用Internet上的公網服務器中轉數據,但對於大數據量的中轉,顯然受到服務器和網絡的負載極限的限制。
第三:依靠Internet上的公網服務器做“媒人”,將這兩臺分別處於不同局域網的機器相互介紹給對方,在它們建立連接之後,服務器即脫離關係。這種方式下,服務器把A的NAT端口映射關係告訴B,又把B的NAT端口映射關係告訴A,這樣AB相互知道對方的端口映射關係之後,就能建立連接。因爲A和B各自的端口映射關係是靠各自的代理網關動態建立的,動態建立的映射端口不得不告知對方。
第四:上面的第三種辦法,也可以採用靜態端口映射方式,這樣就不需要中介服務器對A和B做介紹。在各方的代理網關上,可以在代理工具裏將某個端口(如1350)和局域網內的某臺機器(如內網IP爲200.200.200.100,端口1360)做好靜態映射,這樣,代理網關會自動地將出入於1350端口的數據發往200.200.200.100的1360端口。當然,通信之前,必須對對方的端口映射關係做配置。有多少臺網內機器要通信,就得映射多少個不同的端口,同時在另一個局域網內的機器就要做多少個配置。在局域網內搭建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下載軟件等。

實際上,圍繞P2P通信,尤其是兩個不同局域網間的P2P,已經有許多的P2P協議和開發包湧現。例如,Sun公司以Java寫的開發包Jxta,微軟在Windows XP平臺上有P2P的β版開發包,Intel公佈.Net平臺上的P2P應用開發工具包,放到微軟有關.Net平臺的新聞站點www.gotdotnet.com上供用戶免費下載。

但是利用它們來開發程序,非常繁瑣,我們需要用簡單的實現完成功能就可以了。
如果想研究得更深入仔細,請從Sun公司的網站和微軟網站下載開發包,或者在Google裏
搜索協議和開發包。

下面其實有兩個實例,講述連通的過程,包括簡單僞代碼。
我們不希望在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建立連接失敗,可以循環這個過程,直到一個有限的次數之後,仍不能連接則宣告失敗。

<script type="text/javascript"> var arrBaiduCproConfig=new Array(); arrBaiduCproConfig['uid'] =86602;arrBaiduCproConfig['n'] ='ppcnnetcpr';arrBaiduCproConfig['tm'] =30;arrBaiduCproConfig['cm'] =68;arrBaiduCproConfig['um'] =34;arrBaiduCproConfig['w'] =468;arrBaiduCproConfig['h'] =60;arrBaiduCproConfig['wn'] =2;arrBaiduCproConfig['hn'] =1;arrBaiduCproConfig['ta'] ='right';arrBaiduCproConfig['tl'] ='bottom';arrBaiduCproConfig['bu'] =0;arrBaiduCproConfig['bd'] ='#CC0000';arrBaiduCproConfig['bg'] ='#ffffff';arrBaiduCproConfig['tt'] ='#0000ff';arrBaiduCproConfig['ct'] ='#000000';arrBaiduCproConfig['url'] ='#666666';arrBaiduCproConfig['bdl'] ='#ffffff';arrBaiduCproConfig['rad'] =1;</script><script src="http://cpro.baidu.com/cpro/ui/ui.js" type="text/javascript"> </script><script type="text/javascript"> </script> 

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1076160

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章