[Networking] Hole punching 打洞

Intro

Hole punching 主要用於 P2P(peer-to-peer) 連線
因為 IPv4 可用 IP address 數量上的問題
而有了 NAT 的出現
雖然 NAT 大幅減緩了分配的問題
但 NAT 的出現卻造成建立 P2P 連線上的困難
於是就有了 hole punching 這個技術的出現
(應該只是算是小技巧

原理

條件

首先要有一個 Server
有固定 IP 或有 DDNS
總之這個 Server 需要是所有 peer 都知道的
可以在 NAT 底下但要有設定好的 port forwarding

然後假設現在有 2 個 peer 要連線
叫 A 和 B 好了
A 在 LAN 中的 IP address 是 192.168.0.2
A 的對外 IP address 是 1.0.0.1
B 在 LAN 中的 IP address 是 172.16.0.5
B 的對外 IP address 是 1.0.0.2

但 A 和 B 都互相不知道對方的 IP 和會從哪個 port 送出信息
也就是說 A 不知道 B 在 1.0.0.2
B 不知道 A 在 1.0.0.1
在 A 開啟一個連線的時候
作業系統會分配給程序用的 port 也是未知的
(除非指定要在哪個 port 上監聽進來的封包)
基於 NAT 的特性
外部的 port 是隨機分配的
所以也不會知道是從哪個 port 出來

獲得對外 port

因為 Server 是大家都知道的
所以大家都可以找的到它

當 A 連線到 Server 的時候
A 的 NAT 會開啟一個專屬的對外 port 給 A 用
這個 port 是暫時性的而且每次都不一樣
第幾個 port A 也不會知道
假設 A 這台電腦從 192.168.0.2:50000 發出封包
遇到 NAT 的時候可能 1.0.0.1:50000 已經被其他人用了
這時可能就被分配一個 1.0.0.1:50001 之類的

在信息傳送到 Server 之後
Server 就可以知道 A 是從 1.0.0.1:50001 傳來的
B 在傳信息到 Server
Server 就拿到 B 的信息
這裡假設 B 從 1.0.0.2:60000

這時 Server 就有了 A 和 B 的 IP address 和 port 了

交換信息

Server 把 A 的信息傳送給 B (IP address 和 port)
B 的給 A
這樣互相就有對方的信息

現在 A 知道 B 在 1.0.0.2:60000
B 知道 A 在 1.0.0.1:50001

完成打洞 hole punching

這時 A 通過剛剛從 Server 獲得的資訊將信息傳送給 B
A 要用同一個 port(192.168.0.2:50000) 送到 B
B 從 172.16.0.5:60000 傳送給 A

如果 A 有收到來自 1.0.0.2:60000 的信息
B 有收到來自 1.0.0.1:50000 的信息
那現在 A 和 B 就可以互通了

Demo

我用 Java 的 DatagramSocket 做了 UDP 的打洞

代碼放在 https://github.com/yanzhen0610/p2p-java

Show Comments