基于P2P协议通信模式的选型(一)
基本上,使用.NET進行網絡通信已經非常簡單了,豐富強大的網絡編程類庫的支持,讓一個初學者可以照著幾個簡短的SampleCode,鼓搗幾個諸如Socket、TcpListener類型的對象,加上open、send、accept、close這么幾個方法,不用半天的功夫就能翻出個新版來。然而更多的新手程序員面臨的問題是,拿著這些實驗室里產出的P2P通訊模塊,一拿到真實的應用中,就完全啞火了。造成這種情況的最根本原因,就是因為那些實驗室代碼的調試環境是在一個理想的網段內的,而實際的Internet(甚至一些企業的Intranet)中,NATs、Firewalls無處不在,這些設備(有名為“middleboxes”)使得位于其后方的終端共享同一個公共IP,以此來節約網絡資源,同時建立一個不對稱的連接與尋址模式:即位于middleboxes之后的終端可以輕松地連接處于公網上的地址(資源),但是位于公網上的終端卻很難訪問到middleboxes之后的終端。除非是管理員做了相應的設置,如為NAT配置端口映射,否則發往middleboxes身后終端的數據包會被middleboxes丟棄掉。
矛盾點在于,一個實際的應用,往往要面對的,就是這樣一個復雜地網絡環境,比如說一個P2P的聊天平臺,在Internet應用中,聊友A跟聊友B幾乎不可能是同處在同一個內網環境下的,反倒更有可能的是A與B都是分別處于不同的middleboxes之后。更何況,即使是在單純的網絡環境下,現在也幾乎沒有人的PC不開防毒軟件跟防火墻地裸奔的,否則基本后果很嚴重。基于A與B所處的不同環境,針對一些常見的相關應用,文章羅列了一下具體的實現方法與操作原理。
1、中繼傳輸(Relaying)
鑒于NAT與Firewall在不做明確特殊性配置的時候,是會無條件拒絕任何TCP連接的,因此,對于TCP連接的連臺主機,基本采用的方式,是在公共網絡暴露一個服務端S,A與B想要進行相互的通信,雖然middleboxes阻止他們相互找到對方,但是A與B卻都是可以主動連接到公共網絡上的S,這樣,在S上開端口監聽A、B的請求,建立Tcp連接之后,將AB需要互換的信息通過已經建立起來并被維護的兩條TCP連接進行傳輸。這是最可靠,但是資源開銷最大的方式,Tcp的特性決定了在建立連接和發送數據的時候效率比較低,并不適用于一些例如語音、視頻等實時信息的傳送。當然,這種模式在UDP協議上也是可以應用的,但是共同的弊端是,一旦并發訪問量大的時候,S將會面臨巨大的轉發壓力,網絡已經運算性能都將難以支撐。
顯而易見,廉價的方式是,暴露一個服務器S,S來協助A,B建立一個連接,然后讓AB對等地在建立的連接上交換數據包,并與S無關。這也是P2P的初衷所在。
2、逆向連接(Connection reversal)
當A和B其中一個是暴露在公網上的,而另外一個處于middleboxes之后,假定A在middleboxes之后,B暴露,也即是當A知道了B的IP地址和端口之后,就肯定能夠連接到B的,而B即使獲取到了A所在middlebox的IP與端口,也依舊連不上A,因為從B發出來的IP包其實指向的是middlebox,并且將會被middlebox丟棄。這時候A與B想要進行通信,那么我們可以使用逆向連接的方式。逆向連接的方式依然需要一個暴露的且地址端口共知的服務器S,B定時地向S發送報文,報告自己的位置,以及是否要聯絡A,S記錄B的位置以及請求狀態。當A處于被動監聽的時候,A定時地向S發送報文,詢問S有沒有人找它,如果發現有B在找它,則從S處取B上報的位置(IP、端口),然后斷開S,向B發起連接,并在建立的連接上進行數據交換;當A處于主動想要找B的時候,就更簡單了,只需要直接連接S獲取到B的位置,然后同樣斷開S,與B建立對等地連接進行通信。
3、UDP hole punching
這是重點了,因為這個是解決我們所真正面對的問題的開始,即兩個終端都處在middleboxes之后,但是應用實時性需求比較高的時候。這里還需要細分到三種情況下面來討論:位于同一個middlebox之后,還是不同的middlebox之后,甚至于位于多重middleboxes之后。
3.1 終端處于不同的NAT之后Peers behind different NATs
開始之前,我想再提醒大家注意一下這種方法的名稱——是的,是UDP哦。假設一下,終端A在NAT-A(假定middleboxes為cone NAT,因為UDP hole punching是借助cone NAT的特性的)之后,終端B在NAT-B之后,同樣建立一個暴露且共知的服務器S。我們讓AB各自向S發送UDP數據包,匯報自己的位置,其實S收到的,是AB相對應的NAT-A和NAT-B的位置。此時NAT-A與NAT-B分別分配給他們身后的AB以固定的端口號,基于cone NAT的特性,這兩個端口號是可以接受外部的數據并轉發到AB上的,但是發送源就限定了必須是S,此時S向AB回復他們對方的位置——其實是NAT-A、NAT-B的IP地址,以及他們分別用來跟S交互的端口,后面就簡單描述為A、B的位置了。第一步完成了AB位置的相互通知,并且A與S,B與S現在是能夠相互發送UDP數據包了。A先向S發送一個請求,讓S給B發送一個UDP數據,A向S請求完之后,就開始不停地向B發送數據(前期肯定會被擋掉很多,但是堅持),B收到S發過來的請求之后,立刻向A不停的發數據。這樣一來,NAT-A的端口向B發數據,也只接受B發過來的數據,NAT-B的端口向A發數據也只接受A的數據,就此,一個邏輯連接就建立了!
一旦位于middlebox后面的A與B建立了一個直接的P2P連接,我們可以類推出A和B就都可以擔當S,繼續和其它的終端建立連接。當然,若AB中的任意一個或兩個不在middlebox后面時候,UDP hole punching同樣可以建立P2P通訊通道,不需要判斷middlebox的類型,在變動的環境下有很好的兼容性,尤其當終端處于多重NAT后面的時候,Hole punching的能力尤為突出。
3.2終端處于相同的NAT之后Peers behind the same NATs
在相同NAT之后的情況要簡單的多,使用上述的方法也一樣可以實現,問題是,上述的方法,A與B建立了P2P通道之后,他們相互發送的每一包數據,都是經過了NAT-A與NAT-B進行轉發的,白白浪費了資源,因為在相同的NAT下面的終端是可以直接通信的。我們讓A跟B先各自向S發送UDP數據包,包里包含他們在子網下的IP地址和端口,S服務器判斷需要通信的兩個對象AB的外部地址(NAT)來自于同一個,則分別向AB發送對方內部地址,AB收到之后,使用對方的內部地址進行UDP發報,建立P2P通信通道。這樣AB的通信就在內網中進行了,與NAT與S都無關。
3.3 終端處于多級NAT之后(Peers separated by multiple NATs)
以電信這樣的ISP為例,它存在一個巨型的NAT,然后再分到各個省市區到用戶的家里,一層一層。處于多級的NAT之后的AB需要使用UDP進行通信的時候,同樣首先暴露一個共知服務器S,我們所期望的是如同3.1那樣,AB發送到S交換位置信息,最終能夠A向B發送UDP包的同時B也向A發送UDP數據,以此建立連接。事實上,若S是擁有世界最高級別IP地址(非NAT再分配的),當AB同時請求它的時候,他見到的很可能是相同的IP地址——電信的最高層的NAT地址。并且NAT內部地址的分配會因為用戶的設定而發生重復,因此在這種狀況下,往往只能依賴電信最高層的NAT來進行loopback translation,使用S服務器能夠抓的到的外部IP進行數據交互。
最后,還是要再次提醒,UDP hole punching技術是基于UDP的,依賴于cone NAT特性的一種技術。也就是說終端之上的NAT必須都是cone NAT才可以,因為在cone NAT下,只要UDP還在使用,NAT就會保持住外網IP+端口與內網主機IP+端口的綁定,這恰恰是之前UDP通信通道可以建立的基礎。Cone NAT的應用是非常普遍的,因此,UDP hole punching技術的應用也同樣的普遍,但是在非cone NAT配置下,如對等NAT的時候,這種技術就會失效不可用。
最近服務系統升級,動不動就是在機房一整天,實在是太累,太困了,明天繼續….
轉載于:https://www.cnblogs.com/bobolink/archive/2010/07/28/1787429.html
總結
以上是生活随笔為你收集整理的基于P2P协议通信模式的选型(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ORACLE常用命令【转】
- 下一篇: Windows服务程序的调试