Skip to content

NAT 机制与内网穿透

2011 年,全世界最后一个 /8 公网 IPv4 地址块被分配完毕。

此后,新入网的设备再也无法获得公网 IP。但你可能没有感觉——手机能用 WiFi,电脑能上网,云服务器也能访问。

这一切的功臣,就是 NAT(Network Address Translation)——网络地址转换。

NAT 的诞生背景

IPv4 地址不够用,这是老生常谈了。但问题来了:既然地址不够,为什么我们还能正常上网?

答案是:不是每台设备都需要公网 IP。

私有 IP 地址(10.x.x.x、172.16.x.x-31.x.x、192.168.x.x)可以无限使用,它们不能直接上公网,但可以通过 NAT 转换,共享少量公网 IP。

没有 NAT: 每台设备需要一个公网 IP → 地址不够
有 NAT:   无数私有 IP 共享几个公网 IP → 勉强够用

NAT 的工作原理

基本概念

NAT 在路由器(或防火墙)上工作,把私有 IP + 私有端口映射为公网 IP + 公网端口。

内网主机(192.168.1.100:5000)想访问百度(8.8.8.8:80)

                      ┌─────────────┐
私有 IP ──────────────>│             │
192.168.1.100:5000 ──>│   路由器    │───> 公网 IP 114.114.114.114:6000 ──> 百度
                      │  (NAT)     │
                      │ 192.168.1.1│
                      └─────────────┘

返回时:百度 → 114.114.114.114:6000 → 路由器 → 192.168.1.100:5000 → 内网主机

转换过程

bash
# NAT 转换表(简化示例)
┌────────────────────────────────────────────────────────────────┐
                    NAT 转换表
├─────────────────────────────┬──────────────────────────────────┤
 内网 (IP:Port)              │ 公网 (IP:Port)                    │
├─────────────────────────────┼──────────────────────────────────┤
 192.168.1.100:5000 114.114.114.114:6000
 192.168.1.101:5001 114.114.114.114:6001
 192.168.1.102:5002 114.114.114.114:6002
└─────────────────────────────┴──────────────────────────────────┘

出方向转换

  1. 内网主机发送数据包:源 IP=192.168.1.100,源端口=5000
  2. NAT 设备从公网 IP 池中选一个可用端口(如 6000)
  3. 把源 IP 改为公网 IP,源端口改为 6000
  4. 在转换表中记录这个映射

入方向转换

  1. 收到返回数据:目的 IP=114.114.114.114,目的端口=6000
  2. 查 NAT 表,找到对应内网 IP=192.168.1.100,端口=5000
  3. 把目的 IP 和端口改为内网地址
  4. 转发给内网主机

NAT 的类型

静态 NAT(Static NAT)

一对一映射,公网 IP 和私有 IP 永久绑定。

192.168.1.100 ↔ 114.114.114.114:8000
192.168.1.101 ↔ 114.114.114.114:8001

特点

  • 公网可以直接访问内网服务器
  • 需要多个公网 IP
  • 适用于需要固定公网 IP 的服务器

动态 NAT(Dynamic NAT)

从公网 IP 池中动态分配,用完释放。

首次请求:192.168.1.100 → 分配 114.114.114.114:8000
二次请求:192.168.1.101 → 分配 114.114.114.114:8001
...
IP 池用完:新请求必须等待旧映射过期

特点

  • 不需要多个公网 IP
  • 公网无法主动访问内网
  • 适用于普通上网用户

PAT(端口地址转换)

最常用的 NAT 类型,也叫端口复用 NAT

多个内网 IP 共享一个公网 IP,靠不同端口区分。

192.168.1.100:5000 → 114.114.114.114:6000
192.168.1.101:5000 → 114.114.114.114:6001
192.168.1.102:5000 → 114.114.114.114:6002

特点

  • 最节省公网 IP
  • 目前家庭和企业网络的主流方案
  • 通常说的「NAT」指的就是 PAT

NAT 的问题

1. P2P 连接困难

NAT 的设计初衷是让内网设备主动访问公网,公网设备无法主动连接内网

这对于 P2P 应用(BT 下载、视频通话、游戏)是灾难性的。

解决方案

  • NAT 穿透技术(如 STUN、TURN、ICE)
  • 中继服务器(牺牲连接效率,但保证可用)

2. 端到端连接破坏

NAT 隐藏了端点的真实地址,破坏了互联网的端到端透明性。

理想的互联网应该是「任何主机可以和任何其他主机直接通信」,NAT 打破了这个设计哲学。

3. 日志追踪困难

由于多个用户共享 IP,当发生网络攻击或违法内容传播时,溯源变得困难。

内网穿透:让公网访问内网

NAT 虽然保护了内网,但也带来了「外网无法访问内网服务」的问题。内网穿透就是解决这个问题的。

方法一:端口映射(最简单)

在路由器上配置端口映射,把内网端口暴露到公网。

配置:内网 192.168.1.100:8080 → 公网 114.114.114.114:80
效果:访问 http://114.114.114.114 就能访问内网服务
bash
# 路由器端口映射配置示例
# WAN 接口:任意
# 内部主机:192.168.1.100
# 内部端口:8080
# 外部端口:80
# 协议:TCP

方法二:DMZ 主机

把内网的一台主机设为 DMZ(Demilitarized Zone),它的所有端口都对公网开放(除了已映射的端口)。

DMZ 主机:192.168.1.200
效果:公网可以直接访问 192.168.1.200 的所有端口
风险:DMZ 主机暴露在公网,安全性降低

方法三:NAT 穿透技术(STUN/TURN/ICE)

适用于 P2P 应用,核心思想是:

  1. STUN:让内网主机知道自己被 NAT 后的公网地址
  2. TURN:当 P2P 直连失败时,通过中继服务器转发
  3. ICE:综合 STUN 和 TURN,优先尝试直连,失败后中继
NAT 穿透流程:

1. 两端都连接 STUN 服务器,获取自己的公网地址
2. 双方交换地址信息
3. 尝试直连(同时发送 UDP 包穿透 NAT)
4. 如果直连失败,使用 TURN 服务器中继

方法四:内网穿透工具

市面有很多成熟的内网穿透工具:

frp(Go):高性能内网穿透工具,支持 TCP/UDP/HTTP
ngrok:云服务,开箱即用,免费版有限制
花生壳:老牌工具,商业化成熟
cpolar:新兴工具,免费额度友好

frp 使用示例

服务端(公网机器)

ini
# frps.ini
[common]
bind_port = 7000        # frp 服务端口
vhost_http_port = 8080  # HTTP 服务监听端口
bash
./frps -c frps.ini

客户端(内网机器)

ini
# frpc.ini
[common]
server_addr = 你的公网IP
server_port = 7000

[ssh]
type = tcp
local_ip = 127.0.0.1
local_port = 22
remote_port = 6000
bash
./frpc -c frpc.ini

效果

  • 内网 SSH:localhost:22
  • 公网访问:ssh -p 6000 user@公网IP

方法五:反向代理(VPN)

通过 VPN 隧道,把公网机器拉进内网。

                      VPN 隧道
公网机器 ════════════════════════════ 内网
   │                                    │
   │                                    ├── 192.168.1.100:8080
   │                                    ├── 192.168.1.101:3306
   └── 可以直接访问内网任意服务           └── ...

常见方案:OpenVPN、WireGuard、Tailscale(ZeroTier 的竞品)

实际案例

家庭网络拓扑

Internet

   │ 光纤

┌────────┐
│ 光猫    │  (可能内置路由功能)
└───┬────┘

    │ LAN (192.168.1.x)

┌────────────┐
│  路由器    │  ← NAT + DHCP + 端口映射
│ 192.168.1.1│
└───┬────────┘

    ├── 手机 (192.168.1.101) ─── NAT 转换 ──> 公网 IP:随机端口
    ├── 电脑 (192.168.1.102) ─── NAT 转换 ──> 公网 IP:随机端口
    └── NAS (192.168.1.200) ← 端口映射 21870 ──> 公网 IP:21870

Java 获取本机公网 IP

java
import java.net.HttpURLConnection;
import java.net.URL;

public class PublicIpFinder {
    public static void main(String[] args) {
        try {
            // 使用第三方服务获取公网 IP
            URL url = new URL("https://api.ipify.org");
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(3000);

            try (java.io.BufferedReader br = new java.io.BufferedReader(
                    new java.io.InputStreamReader(conn.getInputStream()))) {
                String publicIp = br.readLine();
                System.out.println("公网 IP: " + publicIp);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

面试追问方向

  • NAT 的工作原理是什么?为什么需要 NAT?
  • NAT 有哪几种类型?区别是什么?
  • NAT 会带来哪些问题?
  • 什么是端口映射?和 DMZ 的区别是什么?
  • 内网穿透有哪些方法?
  • STUN、TURN、ICE 分别解决什么问题?
  • frp 是如何实现内网穿透的?

基于 VitePress 构建