1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
---
這是一個非常棒的問題,直指網路通信的核心原理!你精準地抓住了問題的關鍵:「既然是出站的訊息,為什麼能『接收』?」

這背後的奧秘在於 **NAT (Network Address Translation) 的「狀態追蹤」機制**。

### 內網裝置向網際網路提出 Request 時,實際上發生了什麼?

我們以你的樹莓派瀏覽外部網頁為例,或者更貼近 Ngrok 的情況,以樹莓派向 Ngrok 服務器建立「隧道」為例。

1. **初始請求 (Outbound Request):**
* 假設樹莓派 (內網 IP: `192.168.1.100`,內網源埠: `50000`) 想要連接到 Ngrok 服務器 (公網 IP: `203.0.113.1`, 目標埠: `443` HTTPS)。
* 樹莓派發出一個 TCP/IP 封包:
* 源 IP: `192.168.1.100`
* 源埠: `50000`
* 目標 IP: `203.0.113.1`
* 目標埠: `443`

2. **到達路由器 (NAT 設備):**
* 這個封包到達你的路由器(假設路由器的 WAN 口,即對外面向網際網路的接口,其 IP 是 `203.0.113.2`)。
* 路由器知道這個封包要發到外部網際網路。

3. **NAT 轉換發生:**
* 這是關鍵步驟。路由器在發送封包到網際網路之前,會對封包的源 IP 地址和源埠進行轉換。
* 路由器會從它自己的一個可用公共埠範圍中,選擇一個**臨時的、未被佔用的埠**(例如 `60000`)。
* 路由器會**記錄下**這個轉換資訊,儲存在一個叫做 **NAT 表 (NAT Table) 或連接表 (Connection Table)** 的地方。這個條目可能看起來像這樣:
```
內網源IP:192.168.1.100 | 內網源埠:50000 <--> 外網源IP:203.0.113.2 | 外網源埠:60000
```
* 然後,路由器修改封包:
* 源 IP: 從 `192.168.1.100` 變為 `203.0.113.2` (路由器的 WAN IP)
* 源埠: 從 `50000` 變為 `60000` (路由器選的臨時埠)
* 目標 IP/埠不變:`203.0.113.1:443`
* 這個修改後的封包被發送到網際網路。

4. **網際網路上的服務器接收並回應:**
* Ngrok 服務器 (`203.0.113.1`) 收到來自 `203.0.113.2:60000` 的請求。
* Ngrok 服務器處理請求後,生成一個響應封包,目標是 `203.0.113.2:60000`。
* 響應封包:
* 源 IP: `203.0.113.1`
* 源埠: `443`
* 目標 IP: `203.0.113.2`
* 目標埠: `60000` (路由器的臨時埠)

5. **響應封包返回路由器:**
* 這個響應封包到達你的路由器 (`203.0.113.2`)。
* 路由器接收到封包,發現目標埠是 `60000`。

6. **NAT 反向轉換:**
* 路由器會查詢它的 **NAT 表/連接表**。它會查到:
* 發往 `203.0.113.2:60000` 的響應,應該映射回 `192.168.1.100:50000`。
* 路由器根據 NAT 表中的記錄,再次修改封包:
* 目標 IP: 從 `203.0.113.2` 變為 `192.168.1.100`
* 目標埠: 從 `60000` 變為 `50000`
* 源 IP/埠不變:`203.0.113.1:443`
* 這個修改後的封包被發送到你的內部網路。

7. **樹莓派接收響應:**
* 樹莓派最終收到了來自 Ngrok 服務器的響應,並且這個響應的目標 IP 和埠(`192.168.1.100:50000`)正是它最初發出請求時使用的源 IP 和埠。

### 這就是「狀態追蹤」和「連接狀態」:

* 路由器(NAT 設備)並非簡單地對 IP 地址進行一對一轉換。它是一個**有狀態的設備 (Stateful Device)**。
* 當內部設備主動向外發出連接請求時,路由器會為這個**連接(會話)**在 NAT 表中創建一個條目。
* 這個條目記錄了這次「出站」連接的**狀態**。只要這個連接還「活著」(通常有超時時間),路由器就知道所有從外部返回、且目標埠是它為這次連接分配的臨時埠的封包,都應該被**反向映射**回最初發起請求的那個內部設備。
* 因此,**「向外的請求可以接受向內的訊息」**,是因為這些「向內的訊息」實際上是針對之前「向外的請求」的**回應**。路由器透過其 NAT 表中的狀態資訊,精確地知道這些回應屬於哪個內部連接,並將其正確地轉發回來。

### Ngrok 如何利用這個原理?

Ngrok 的客戶端(在樹莓派上)正是利用這個機制,**主動與 Ngrok 服務器建立一個持久的 TCP 連接**。這個持久連接在路由器的 NAT 表中會保持一個活躍的條目。

當外部用戶訪問 Ngrok 服務器上的公共 URL 時,Ngrok 服務器會將這些請求,**通過這個由樹莓派主動建立的、活躍的 TCP 連接(隧道),反向地「推」回給樹莓派上的 Ngrok 客戶端**。對路由器來說,這依然是已經建立的合法連接(隧道)上的數據流,所以它會允許這些數據進入。

這與「連接埠轉發」的區別在於:
* **連接埠轉發:** 路由器**無條件地**開放一個埠,允許**任何外部主動發起的連接**進入。它不依賴於內部設備是否有「請求」過。
* **NAT 狀態追蹤:** 路由器只允許外部**對內部發出的請求的回應**進入。如果沒有先有內部發出的請求,外部就無法主動連接進來。

這就是 NAT 的精妙之處,它既保護了內網,又允許內網設備與外部進行雙向通信。