1. 程式人生 > >UDP與TCP穿洞技術學習筆記

UDP與TCP穿洞技術學習筆記





TCP


服務端的埠 複用為 客戶端的埠  ( 服務端傳送無歧義, 服務端接收無歧義, 客戶端傳送無歧義, 客戶端接收無歧義)


服務端的埠 複用為 服務端的埠   正常現象


客戶端的埠 複用為 服務端的埠   (客戶端傳送無歧義, 客戶端傳送無歧義,服務端傳送無歧義,  )


客戶端的埠 複用為 客戶端的埠   無歧義


TCP埠複用要求地址不能相同(採用多網絡卡技術,可以是虛擬網絡卡或硬體網絡卡混搭,或者用IP alias技術)。


NAT記錄的是 內網源客戶端IP和內網源客戶端埠,所以NAT收到的TCP包裡的路由器公網IP只會被重新設定為內網源客戶端IP,所以複用的新套接字(新套接字使用了新的內網IP)是無法收到這個訊息的,所以外網的TCP客戶端的握手都無法被轉達了,那就根本不可能實現通訊了。但是埠仍然被複用了,只不過NAT閘道器無法起到對映功能而已。(除非NAT能夠辨別出,複用後的內網主機身份,這個需要NAT裝置增強)


每次複用都會殺死複用前的套接字。


如果複用前的套接字是客戶端套接字,那麼這個套接字也是設定為SO_REUSEADDR


服務端的套接字通常被設定為SO_REUSEADDR,這個是否安全呢?如果不設定的話,套Listen階段被殺死,埠會不會洩露(脫離作業系統管理)呢?答:我覺得不安全,低許可權的程序會在高許可權的程序伺服器以外死亡時獲得高許可權的套接字,但是沒辦法,否則會造成洩露。


sock在什麼情況下會TIME_WAIT?


為什麼TCP無法做到允許地址一樣的埠複用?防止不安全的SO_REUSEADDR問題變得更加惡化(高許可權的程序套接字可能會隨意被擠掉)。




在TCP中設定為SO_REUSEADDR的套接字,僅能起到【當套接字未被釋放(仍然存活於核心的套接字檔案快取中)時,最後一個擁有能釋放套接字權利的程序被殺死了(這種套接字就跟孤兒一樣)】,這次SO_REUSEADDR的套接字能被新的套接字通過埠複用(IP不一樣)將它殺死。(要實現穿洞需要增強NAT的功能)


UDP  


在繫結的時候是半個套接字。只有在收發時建立臨時套接字。


UDP 在多播模式下埠複用允許IP地址也一樣,這些套接字並不是所有都能讀取資訊,只有最後一個套接字會正常接收資料。


UDP實現NAT穿洞之後,複用前的套接字會失效(可以用來殺死孤兒套接字,也可以用來實現內網穿透)。










NAT究竟是怎樣的技術?


sock在什麼情況下會TIME_WAIT?




BSD系統引入了SO_REUSEPORT引數,該引數用於區分埠重用還是地址重用,在這樣的系統裡面,上述所有的引數必須都設定才行。






結論:TCP不能做NAT穿洞,UDP可以。TCP要實現穿洞只能依賴於隧道技術,即通過VPN轉發  或  只用超級NAT功能。


超級NAT能夠辨識同一主機上的不同IP,方法就是在第一個訪問NAT的時候頒發一枚身份證,之後不管什麼IP訪問NAT都必須帶上這個身份證(用於標識主機的身份,達到識別主機唯一性的能力)。或者讓內網伺服器套接字具有傳送syn的功能,先往想要連結自己伺服器的Client傳送一個syn(呼叫的非同步connect()就會發送syn了).


具體實現看下面


原文:http://www.cnblogs.com/liu-q/p/4158213.html






現在來看更加實際的一種情景,A與B分別位於不同的NAT裝置後面,並且假定埠號是TCP協議的埠號,而不是UDP的埠號。客戶端向彼此公網endpoint發起連線的操作,會使得各自的NAT裝置開啟新的"洞"允許A與B的TCP資料通過。如果NAT裝置支援TCP"打洞"操作的話,一個在客戶端之間的基於TCP協議的流通道就會自動建立起來。如果A向B傳送的第一個SYN包發到了B的NAT裝置,而B在此前沒有向A傳送SYN包,B的NAT裝置會丟棄這個包,這會引起A的"連線失敗"或"無法連線"問題。而此時,由於A已經向B傳送過SYN包,B發往A的SYN包將被看作是由A發往B的包的迴應的一部分,所以B發往A的SYN包會順利地通過A的NAT裝置,到達A,從而建立起A與B的p2p連線。


三. 從應用程式的角度來看TCP"打洞"


從應用程式的角度來看,在進行TCP"打洞"的時候都發生了什麼呢?假定A首先向B發出SYN包,該包發往B的公網endpoint,並且被B的NAT裝置丟棄,但是B發往A的公網endpoint的SYN包則通過A的NAT到達了A,然後,會發生以下的兩種結果中的一種,具體是哪一種取決於作業系統對TCP協議的實現:


(1)A的TCP事先會發現收到的SYN包就是其發起連線並希望聯入的B的SYN包,通俗一點來說就是"說曹操,曹操到"的意思,本來A要去找B,結果B自己找上門來了。A的TCP協議棧因此會把B做為A向B發起連線connect的一部分,並認為連線已經成功。程式A呼叫的非同步connect()函式將成功返回,A的listen()等待從外部聯入的函式將沒有任何反映。此時,B聯入A的操作在A程式的內部被理解為A聯入B連線成功,並且A開始使用這個連線與B開始p2p通訊。


由於收到的SYN包中不包含A需要的ACK資料,因此,A的TCP將用SYN-ACK包迴應B的公網endpoint,並且將使用先前A發向B的SYN包一樣的序列號。一旦B的TCP收到由A發來的SYN-ACK包,則把自己的ACK包發給A,然後兩端建立起TCP連線。簡單的說,第一種,就是即使A發往B的SYN包被B的NAT丟棄了,但是由於B發往A的包到達了A。結果是,A認為自己連線成功了,B也認為自己連線成功了,不管是誰成功了,總之連線是已經建立起來了。


(2)另外一種結果是,A的TCP實現沒有像(1)中所講的那麼"智慧",它沒有發現現在聯入的B就是自己希望聯入的。就好比在機場接人,明明遇到了自己想要接的人卻不認識,誤認為是其它的人,安排別人給接走了,後來才知道是自己錯過了機會,但是無論如何,人已經接到了任務已經完成了。然後,A通過常規的listen()函式和accept()函式得到與B的連線,而由A發起的向B的公網endpoint的連線會以失敗告終。儘管A向B的連線失敗,A仍然得到了B發起的向A的連線,等效於A與B之間已經聯通,不管中間過程如何,A與B已經連線起來了,結果是A和B的基於TCP協議的p2p連線已經建立起來了。


第一種結果適用於基於BSD的作業系統對於TCP的實現,而第二種結果更加普遍一些,多數linux和windows系統都會按照第二種結果來處理。


 


 


對於tcp打洞這回事,我感覺就是有點像忽悠,忽悠來忽悠去,忽悠著忽悠著就忽悠上了,呵呵,人生何嘗不是?




LD:謎底是什麼呢?最終還是得看NAT演算法了或者PCP協議

主流的穿洞方法是: UPnP技術和隧道代理技術