1. 程式人生 > >TCP 打洞原理

TCP 打洞原理

UDP打洞技術:

對於兩個peer,A和B。
1、若A和B位於同一個nat之後。如果nat支援迴環轉換,A和B之間打洞時使用彼此的外網地址是可以連通的。但是最好是優先嚐試內網連線。
2、若A和B位於不同的nat之後。若兩個nat都是公網地址,則屬於“典型”連線過程,連線相對簡單可靠。
3、若A和B位於多級nat之後。
a)A和B有相同的出口nat。則使用內網地址連線,可能會存在明顯錯誤(需要鑑別)。若頂級nat不支援迴環轉換,則連線會失敗。
b)A和B在不同的出口nat後。則過程類似情況2。

4、若A和B只有一方在nat之後。利用反向連線的方法完成。(也可以採用tcp協議連線)。

連線過程:

依據stun協議鑑別出的nat型別,採取合適的連線過程。

兩個nat4之後的peer仍然無法直接完成連線,有需要時可採用TURN協議建立“連線”(通過中繼完成的通訊)。


重要問題:
1、udp會經常收到非預期的資料包,所以需要做過濾和鑑別。比如,雙方連線時,需要從伺服器拿到相同的令牌,資料包提交了這個令牌才認為是有效的。


---------------------------------------------------------------------------------------------------------------------

TCP打洞技術:
tcp打洞也需要NAT裝置支援才行。
tcp的打洞流程和udp的基本一樣,但tcp的api決定了tcp打洞的實現過程和udp不一樣。
tcp按cs方式工作,一個埠只能用來connect或listen,所以需要使用埠重用,才能利用本地nat的埠對映關係。(設定SO_REUSEADDR,在支援SO_REUSEPORT的系統上,要設定這兩個引數。)

連線過程:(以udp打洞的第2種情況為例(典型情況))
nat後的兩個peer,A和B,A和B都bind自己listen的埠,向對方發起連線(connect),即使用相同的埠同時連線和等待連線。因為A和B發出連線的順序有時間差,假設A的syn包到達B的nat時,B的syn包還沒有發出,那麼B的nat對映還沒有建立,會導致A的連線請求失敗(連線失敗或無法連線,如果nat返回RST或者icmp差錯,api上可能表現為被RST;有些nat不返回資訊直接丟棄syn包(反而更好)),(應用程式發現失敗時,不能關閉socket,closesocket()可能會導致NAT刪除埠對映;隔一段時間(1-2s)後未連線還要繼續嘗試);但後發B的syn包在到達A的nat時,由於A的nat已經建立的對映關係,B的syn包會通過A的nat,被nat轉給A的listen埠,從而進去三次握手,完成tcp連線。

從應用程式角度看,連線成功的過程可能有兩種不同表現:(以上述假設過程為例)
1、連線建立成功表現為A的connect返回成功。即A端以TCP的同時開啟流程完成連線。
2、A端通過listen的埠完成和B的握手,而connect嘗試持續失敗,應用程式通過accept獲取到連線,最終放棄connect(這時可closesocket(conn_fd))。
多數Linux和Windows的協議棧表現為第2種。

但有一個問題是,建立連線的client端,其connect繫結的埠號就是主機listen的埠號,或許這個peer後續還會有更多的這種socket。雖然理論上說,socket是一個五元組,埠號是一個邏輯數字,傳輸層能夠因為五元組的不同而區分開這些socket,但是是否存在實際上的異常,還有待更多觀察。

另外的問題:
1、Windows XP SP2作業系統之前的主機,這些主機不能正確處理TCP同時開啟,或者TCP套接字不支援SO_REUSEADDR的引數。需要讓AB有序的發起連線才可能完成。

上述tcp連線過程,僅對NAT1、2、3有效,對NAT4(對稱型)無效。
由於對稱型nat通常採用規律的外部埠分配方法,對於nat4的打洞,可以採用埠預測的方式進行嘗試。