NAT 和 STUN

缩写 全称
NAT Network Address Translation
STUN Session Traversal Utilities for NAT

一、NAT1~NAT4 通俗解释

先设定一个相同的场景方便对比:

  • 你家内网电脑 A:IP 192.168.1.10,用 UDP 端口 4000 对外通信。
  • 你家路由器公网 IP1.2.3.4
  • 路由器给这个内网会话分配的公网端口,假设第一次是 8000

下面的“外部主机”,我随便起名叫 B,IP 为 5.6.7.8


1. NAT1(完全圆锥形 / Full Cone NAT)

一句话:只要开了门,全世界的任何人都可以进来。

  • 建立映射:
    电脑 A 192.168.1.10:4000 → 经过路由器,映射为公网 1.2.3.4:8000
  • 规则:
    现在,任何外部主机,只要是往 1.2.3.4:8000 发数据包,路由器都会原样转给内网 A。
    • 外部主机 B 5.6.7.8:9999 可以直接发数据给 1.2.3.4:8000,A 能收到。
    • 另一台主机 C 9.9.9.9:12345 也直接发过来,A 一样能收到。
    • A 自己有没有先给 B、C 发过数据,都无所谓。

比喻
你告诉了全世界一个公共信箱号 1.2.3.4:8000,任何人把信扔进去,都会直接送到你手里。


2. NAT2(受限圆锥形 / Restricted Cone NAT)

一句话:我只接收我主动联系过的人的回信,具体是谁我无所谓,只要是那个人所在的公司就行。

  • 建立映射:
    同样映射 1.2.3.4:8000
  • 规则:
    只有当内网 A 先主动向某个外部 IP 发送过数据,这个外部 IP 的所有端口才能回包给 1.2.3.4:8000

举例:

  • A 从 4000 主动发给外部主机 B 5.6.7.8:53 一个数据包。
  • 此时,B 的 IP 5.6.7.8 被允许回信。
    • B 从 53 端口回包 → 通过。
    • B 从 8080 端口回包 → 也能通过(只要 IP 是 5.6.7.8 就行)。
  • 但如果主机 C 9.9.9.9:7777 直接往 1.2.3.4:8000 发数据,因为 A 从未主动给 9.9.9.9 发过东西,所以数据被路由器丢弃

比喻
你主动给“B 公司”打了个电话,之后只要来电显示是 B 公司的总机号码(IP 相同),无论他们用哪个分机(端口)打回来,你都会接。但陌生人打来的一律不接。


3. NAT3(端口受限圆锥形 / Port Restricted Cone NAT)

一句话:我只接收我主动联系过的那个人、用那个特定的分机打回来的电话。

  • 建立映射:
    映射 1.2.3.4:8000
  • 规则:
    只有当内网 A 先主动向某个外部 IP:端口 发送过数据,这个固定的 IP 和端口 才能回包。

举例:

  • A 主动发给 B 5.6.7.8:53
  • 此时,只有 5.6.7.8:53 回包到 1.2.3.4:8000 才能被 A 收到。
    • 5.6.7.8:8080 回包 → 被丢弃(端口不对)。
    • 其他 IP 发来的更不用想。
  • A 如果想收 5.6.7.8:8080 的数据,必须先主动给 5.6.7.8:8080 发个包。

比喻
你打给 B 公司的张三(分机 53),之后就只接张三用他座机(端口 53)打回来的电话。李四用分机 8080 打过来,你不认识,不接。


4. NAT4(对称型 / Symmetric NAT)

一句话:每次给不同的人打电话,前台就给你换一个陌生的外线号码。别人只能打回那个特定的外线号码才能找到你。

  • 映射特点:
    同一个内网 192.168.1.10:4000给不同的目标发数据,路由器分配的对外端口都不一样

举例:

  • A 发给主机 B 5.6.7.8:53,路由器分配映射 1.2.3.4:8000且只有 5.6.7.8:53 回复到 8000 才有效。
  • A 又发给主机 C 9.9.9.9:1234,路由器会另开一个新端口,比如 1.2.3.4:8001
    这时 C 必须回复到 8001 才能被 A 收到;B 如果错发到 8001,会被丢弃;C 发到 8000 也是丢弃。

比喻
这就像一个极其严格的电话总机:
你给张三打电话,总机显示的来电号码是 8000
你给李四打电话,总机换了个号码显示成 8001
张三只能用 8000 回拨,李四只能用 8001 回拨,两个人用错号码都找不着你。


二、STUN 内网穿透到底在干什么?

STUN 就是“公网镜子”,帮你查出:你在互联网上看起来长什么样(公网 IP + 端口),以及你的 NAT 是什么脾气。

1. 基本流程(配合实际 IP/端口)

设定:

  • STUN 服务器:3.3.3.3:3478
  • 内网电脑 A:家里,通过路由器,公网 IP 1.2.3.4
  • 内网电脑 B:另一个地方,公网 IP 5.6.7.8

第 1 步:照镜子
A 向 3.3.3.3:3478 发一个查询包。
STUN 服务器告诉 A:
“你在公网上的地址是 1.2.3.4:8000,你的 NAT 类型是 XXX。”

B 也去照,得到自己的公网地址:5.6.7.8:9000

第 2 步:交换地址
A 和 B 通过某个聊天服务器(信令服务器)互相告诉对方:
“我的公网地址是 1.2.3.4:8000
“我的公网地址是 5.6.7.8:9000

第 3 步:互相打洞
A 和 B 同时向对方的公网地址发 UDP 数据包:

  • A 往 5.6.7.8:9000 发。
  • B 往 1.2.3.4:8000 发。

这个动作的目的,是让各自的 NAT 路由器“以为”双方正在通信,从而允许后续对方的数据进入。这就是常说的 UDP 打洞


三、针对 NAT1~NAT4,STUN 打洞的结果如何?

仍然用上面的 A (1.2.3.4:8000) 和 B (5.6.7.8:9000)。

✅ NAT1(完全圆锥型)

  • 场景:A 是 NAT1,或者双方都是 NAT1。
  • 过程:A 通过 STUN 拿到 1.2.3.4:8000 告诉 B。B 完全不需要做什么打洞准备,直接往 1.2.3.4:8000 发数据,A 就能收到。因为 NAT1 无论谁来都能进。
  • 结果直接穿透成功,最容易。

✅ NAT2(受限圆锥型)

  • 过程
    1. A、B 交换地址后,如果 B 直接往 1.2.3.4:8000 发数据,会被 A 的路由器丢掉(因为 A 还没给 B 发过包)。
    2. 于是执行打洞:A 主动往 5.6.7.8:9000 发送一个“打洞包”。这个包到了 B 的路由器虽然会被丢弃,但 A 的路由器已经记录了“允许 IP 5.6.7.8 的数据进入”。
    3. 紧接着 B 往 1.2.3.4:8000 发数据包,这一次 A 的路由器看到源 IP 是 5.6.7.8,就放行了。
    4. 此时 B 的数据成功进入 A,双向通道建立。
  • 结果可以通过打洞穿透成功。
    NAT2 只检查 IP,B 用任意端口回复都可以。

✅ NAT3(端口受限圆锥型)

  • 过程:和 NAT2 类似,但对端口有要求。
    1. A 先往 5.6.7.8:9000(注意必须是 B 告知的端口)发一个打洞包。
    2. A 的路由器这时只允许 5.6.7.8:9000 回复。
    3. B 再用 5.6.7.8:90001.2.3.4:8000 发数据,刚好符合“IP+端口都匹配”,打洞成功。
  • 关键:B 使用的源端口必须是 A 发送时的目标端口。而 B 一般就拿自己的公网映射端口作为源端口,所以打洞时天然满足这个条件。
  • 结果同样可以打洞穿透成功。

❌ NAT4(对称型)

  • 场景:假设 A 是 NAT4(或双方都是)。
  • 过程
    1. A 向 STUN 查询,获得专用于与 STUN 通信的地址 1.2.3.4:8000
    2. A 把这个 8000 告诉 B。
    3. A 为了打洞,向 B 的地址 5.6.7.8:9000 发了一个包。
      因为 A 是 NAT4,目标变了,路由器立刻为“与 B 通信”分配了一个新端口,比如 1.2.3.4:8001
    4. B 只知道 8000,所以它的打洞包依然发往 1.2.3.4:8000
    5. A 的路由器收到后一看,8000 这个映射只允许 STUN 服务器的 IP 进来,或者根本就不接受 B 的数据;而且真正留给 B 的通道是 8001。结果包被丢弃。
    6. B 无法猜到 8001,穿透失败。
  • 结果STUN 打洞基本无效。
    如果双方都是 NAT4,更不可能打通;即便只有一方是 NAT4,通常也无法穿透。这种情况只能借助 TURN 中继服务器 来转发数据。

四、一句话总结

  • NAT1~NAT3 都属于“圆锥形”NAT,只要 A、B 都不是 NAT4,STUN 打洞几乎必成
  • NAT4(对称型) 每次换目标就换外网端口,让对方无法猜到,STUN 无能为力,必须用中转服务器