NAT、代理服务与内网穿透详解:从地址转换到公网访问内网服务
摘要:IPv4 地址数量有限,私有地址、NAT、代理服务和内网穿透都是围绕"不同网络之间如何通信"衍生出来的关键技术。本文从 NAT 和 NAPT 的转换过程讲起,对比正向代理、反向代理与 NAT 的差异,再解释为什么外网通常无法直接访问内网服务,以及内网穿透的基本思路。
前言
很多人在家里或公司写网络程序时,都会遇到一个现象:
text
局域网内能访问,换到外网就访问不了。
这背后通常和私有 IP、NAT、路由器出口地址有关。NAT 让大量内网设备能够共享公网出口访问互联网,但它也带来了一个副作用:外部网络很难主动连接到内网机器。
代理服务和内网穿透又是在这个背景下经常出现的概念。它们看起来都像"中间转发",但工作层次、解决的问题和部署方式并不一样。
一、为什么需要 NAT
IPv4 地址是 32 位,地址总量有限。为了让更多设备接入网络,很多家庭、学校、公司内部都会使用私有 IP 地址,例如:
text
10.0.0.10
192.168.1.100
172.16.1.20
私有 IP 只需要在当前局域网内不冲突,不要求全网唯一。公网 IP 则必须在互联网范围内唯一。
NAT 的核心作用是:让内网私有 IP 在对外通信时转换成公网 IP。
#mermaid-svg-8N8KkJQYlyMjeNq0{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-8N8KkJQYlyMjeNq0 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8N8KkJQYlyMjeNq0 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8N8KkJQYlyMjeNq0 .error-icon{fill:#552222;}#mermaid-svg-8N8KkJQYlyMjeNq0 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8N8KkJQYlyMjeNq0 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8N8KkJQYlyMjeNq0 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8N8KkJQYlyMjeNq0 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8N8KkJQYlyMjeNq0 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8N8KkJQYlyMjeNq0 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8N8KkJQYlyMjeNq0 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8N8KkJQYlyMjeNq0 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8N8KkJQYlyMjeNq0 .marker.cross{stroke:#333333;}#mermaid-svg-8N8KkJQYlyMjeNq0 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8N8KkJQYlyMjeNq0 p{margin:0;}#mermaid-svg-8N8KkJQYlyMjeNq0 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-8N8KkJQYlyMjeNq0 .cluster-label text{fill:#333;}#mermaid-svg-8N8KkJQYlyMjeNq0 .cluster-label span{color:#333;}#mermaid-svg-8N8KkJQYlyMjeNq0 .cluster-label span p{background-color:transparent;}#mermaid-svg-8N8KkJQYlyMjeNq0 .label text,#mermaid-svg-8N8KkJQYlyMjeNq0 span{fill:#333;color:#333;}#mermaid-svg-8N8KkJQYlyMjeNq0 .node rect,#mermaid-svg-8N8KkJQYlyMjeNq0 .node circle,#mermaid-svg-8N8KkJQYlyMjeNq0 .node ellipse,#mermaid-svg-8N8KkJQYlyMjeNq0 .node polygon,#mermaid-svg-8N8KkJQYlyMjeNq0 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-8N8KkJQYlyMjeNq0 .rough-node .label text,#mermaid-svg-8N8KkJQYlyMjeNq0 .node .label text,#mermaid-svg-8N8KkJQYlyMjeNq0 .image-shape .label,#mermaid-svg-8N8KkJQYlyMjeNq0 .icon-shape .label{text-anchor:middle;}#mermaid-svg-8N8KkJQYlyMjeNq0 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-8N8KkJQYlyMjeNq0 .rough-node .label,#mermaid-svg-8N8KkJQYlyMjeNq0 .node .label,#mermaid-svg-8N8KkJQYlyMjeNq0 .image-shape .label,#mermaid-svg-8N8KkJQYlyMjeNq0 .icon-shape .label{text-align:center;}#mermaid-svg-8N8KkJQYlyMjeNq0 .node.clickable{cursor:pointer;}#mermaid-svg-8N8KkJQYlyMjeNq0 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-8N8KkJQYlyMjeNq0 .arrowheadPath{fill:#333333;}#mermaid-svg-8N8KkJQYlyMjeNq0 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-8N8KkJQYlyMjeNq0 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-8N8KkJQYlyMjeNq0 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8N8KkJQYlyMjeNq0 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-8N8KkJQYlyMjeNq0 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8N8KkJQYlyMjeNq0 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-8N8KkJQYlyMjeNq0 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-8N8KkJQYlyMjeNq0 .cluster text{fill:#333;}#mermaid-svg-8N8KkJQYlyMjeNq0 .cluster span{color:#333;}#mermaid-svg-8N8KkJQYlyMjeNq0 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-8N8KkJQYlyMjeNq0 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-8N8KkJQYlyMjeNq0 rect.text{fill:none;stroke-width:0;}#mermaid-svg-8N8KkJQYlyMjeNq0 .icon-shape,#mermaid-svg-8N8KkJQYlyMjeNq0 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8N8KkJQYlyMjeNq0 .icon-shape p,#mermaid-svg-8N8KkJQYlyMjeNq0 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-8N8KkJQYlyMjeNq0 .icon-shape .label rect,#mermaid-svg-8N8KkJQYlyMjeNq0 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8N8KkJQYlyMjeNq0 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-8N8KkJQYlyMjeNq0 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-8N8KkJQYlyMjeNq0 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 内网主机 10.0.0.10
NAT 路由器
公网服务器 163.221.120.9
从公网服务器看,请求并不是来自 10.0.0.10,而是来自 NAT 路由器的公网地址。
二、NAT 的地址转换过程
假设内网主机 10.0.0.10 访问外网服务器 163.221.120.9,NAT 路由器的公网地址是 202.244.174.37。
出站时:
text
源 IP: 10.0.0.10 -> 202.244.174.37
目的 IP: 163.221.120.9 -> 163.221.120.9
入站响应回来时:
text
源 IP: 163.221.120.9 -> 163.221.120.9
目的 IP: 202.244.174.37 -> 10.0.0.10
NAT 路由器内部会维护一张转换表。第一次从 10.0.0.10 发起对外通信时,路由器会记录这条映射关系;后续响应回来时,再根据映射把目标地址改回内网主机地址。
可以把它理解成:
| 阶段 | NAT 做的事情 |
|---|---|
| 内网访问外网 | 把源私有 IP 改成公网出口 IP |
| 外网响应回来 | 根据转换表把目的公网 IP 改回内网 IP |
| 通信结束 | 删除对应转换关系 |
三、NAPT:为什么还要带端口
只有 IP 地址转换还不够。考虑一个更常见的情况:
text
10.0.0.10 访问 163.221.120.9:80
10.0.0.11 也访问 163.221.120.9:80
外网服务器返回数据时,目的 IP 都是 NAT 路由器的公网 IP。路由器怎么知道应该转给 10.0.0.10,还是转给 10.0.0.11?
这就需要 NAPT。NAPT 使用 IP + port 建立映射关系。
示例转换表:
| 内网连接 | 转换后的公网连接 | 目标服务 |
|---|---|---|
10.0.0.10:1025 |
202.244.174.37:1025 |
163.221.120.9:80 |
10.0.0.11:1025 |
202.244.174.37:1026 |
163.221.120.9:80 |
可以看到,即使两个内网主机使用了相同源端口,NAT 路由器也可以在公网侧改成不同端口,从而区分不同连接。
在 TCP 场景中,连接建立时会生成映射表项,连接断开后会删除表项。这些关系由 NAT 设备自动维护。
四、NAT 的限制
NAT 解决了 IPv4 地址不足问题,但它也带来一些限制。
| 限制 | 说明 |
|---|---|
| 外部难以主动访问内部服务 | 映射表通常由内网主动访问外网时生成 |
| 转换表有维护成本 | 表项创建、查询、销毁都需要开销 |
| NAT 设备成为关键节点 | 设备异常可能导致已有连接全部中断 |
| 端到端连接语义被改变 | 原始源地址在出口处被替换 |
很多"内网服务无法被公网访问"的问题,本质上就来自第一条:没有映射关系时,外部请求到达 NAT 设备,NAT 不知道应该转给哪台内网主机。
五、代理服务是什么
代理服务器也位于客户端和目标服务器之间,也会转发请求和响应,所以它看起来和 NAT 有点像。
但二者关注点不同:
| 对比项 | NAT | 代理服务器 |
|---|---|---|
| 主要目标 | 缓解 IP 地址不足,完成地址转换 | 代表一方发起或接收应用请求 |
| 常见层次 | 网络层附近,改 IP/端口 | 更贴近应用层,理解具体协议 |
| 部署位置 | 路由器、防火墙等出口设备 | 独立服务器或软件服务 |
| 典型用途 | 内网共享公网出口 | 访问控制、缓存、负载均衡、安全隔离 |
NAT 更像网络基础能力,代理更像应用转发能力。
六、正向代理:代理客户端访问目标服务器
正向代理位于客户端和目标服务器之间,代表客户端向目标服务器发请求。
#mermaid-svg-vHr99gmZZZQ887m7{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-vHr99gmZZZQ887m7 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-vHr99gmZZZQ887m7 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-vHr99gmZZZQ887m7 .error-icon{fill:#552222;}#mermaid-svg-vHr99gmZZZQ887m7 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-vHr99gmZZZQ887m7 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-vHr99gmZZZQ887m7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-vHr99gmZZZQ887m7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-vHr99gmZZZQ887m7 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-vHr99gmZZZQ887m7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-vHr99gmZZZQ887m7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-vHr99gmZZZQ887m7 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-vHr99gmZZZQ887m7 .marker.cross{stroke:#333333;}#mermaid-svg-vHr99gmZZZQ887m7 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-vHr99gmZZZQ887m7 p{margin:0;}#mermaid-svg-vHr99gmZZZQ887m7 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-vHr99gmZZZQ887m7 .cluster-label text{fill:#333;}#mermaid-svg-vHr99gmZZZQ887m7 .cluster-label span{color:#333;}#mermaid-svg-vHr99gmZZZQ887m7 .cluster-label span p{background-color:transparent;}#mermaid-svg-vHr99gmZZZQ887m7 .label text,#mermaid-svg-vHr99gmZZZQ887m7 span{fill:#333;color:#333;}#mermaid-svg-vHr99gmZZZQ887m7 .node rect,#mermaid-svg-vHr99gmZZZQ887m7 .node circle,#mermaid-svg-vHr99gmZZZQ887m7 .node ellipse,#mermaid-svg-vHr99gmZZZQ887m7 .node polygon,#mermaid-svg-vHr99gmZZZQ887m7 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-vHr99gmZZZQ887m7 .rough-node .label text,#mermaid-svg-vHr99gmZZZQ887m7 .node .label text,#mermaid-svg-vHr99gmZZZQ887m7 .image-shape .label,#mermaid-svg-vHr99gmZZZQ887m7 .icon-shape .label{text-anchor:middle;}#mermaid-svg-vHr99gmZZZQ887m7 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-vHr99gmZZZQ887m7 .rough-node .label,#mermaid-svg-vHr99gmZZZQ887m7 .node .label,#mermaid-svg-vHr99gmZZZQ887m7 .image-shape .label,#mermaid-svg-vHr99gmZZZQ887m7 .icon-shape .label{text-align:center;}#mermaid-svg-vHr99gmZZZQ887m7 .node.clickable{cursor:pointer;}#mermaid-svg-vHr99gmZZZQ887m7 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-vHr99gmZZZQ887m7 .arrowheadPath{fill:#333333;}#mermaid-svg-vHr99gmZZZQ887m7 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-vHr99gmZZZQ887m7 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-vHr99gmZZZQ887m7 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vHr99gmZZZQ887m7 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-vHr99gmZZZQ887m7 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vHr99gmZZZQ887m7 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-vHr99gmZZZQ887m7 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-vHr99gmZZZQ887m7 .cluster text{fill:#333;}#mermaid-svg-vHr99gmZZZQ887m7 .cluster span{color:#333;}#mermaid-svg-vHr99gmZZZQ887m7 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-vHr99gmZZZQ887m7 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-vHr99gmZZZQ887m7 rect.text{fill:none;stroke-width:0;}#mermaid-svg-vHr99gmZZZQ887m7 .icon-shape,#mermaid-svg-vHr99gmZZZQ887m7 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vHr99gmZZZQ887m7 .icon-shape p,#mermaid-svg-vHr99gmZZZQ887m7 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-vHr99gmZZZQ887m7 .icon-shape .label rect,#mermaid-svg-vHr99gmZZZQ887m7 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vHr99gmZZZQ887m7 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-vHr99gmZZZQ887m7 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-vHr99gmZZZQ887m7 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 客户端
正向代理
目标服务器
工作流程:
- 客户端把请求发送给正向代理;
- 正向代理根据配置处理请求,例如缓存、过滤、访问控制;
- 正向代理把请求转发给目标服务器;
- 目标服务器把响应返回给正向代理;
- 正向代理再把响应返回给客户端。
常见能力:
| 能力 | 说明 |
|---|---|
| 缓存 | 常访问内容可以直接从代理返回 |
| 内容过滤 | 根据规则过滤请求或响应 |
| 访问控制 | 限制特定客户端访问特定网站 |
| 隐藏客户端身份 | 目标服务器看到的是代理地址 |
| 访问管理 | 企业或公共网络中控制出口访问 |
正向代理的核心视角是:服务器不知道真正的客户端是谁,客户端知道自己在使用代理。
七、反向代理:代理后端服务器接收请求
反向代理通常部署在 Web 服务前面。客户端访问的是反向代理,反向代理再根据规则把请求转发给后端服务器。
#mermaid-svg-hZahhe38xEGp3V9r{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-hZahhe38xEGp3V9r .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-hZahhe38xEGp3V9r .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-hZahhe38xEGp3V9r .error-icon{fill:#552222;}#mermaid-svg-hZahhe38xEGp3V9r .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-hZahhe38xEGp3V9r .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-hZahhe38xEGp3V9r .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-hZahhe38xEGp3V9r .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-hZahhe38xEGp3V9r .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-hZahhe38xEGp3V9r .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-hZahhe38xEGp3V9r .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-hZahhe38xEGp3V9r .marker{fill:#333333;stroke:#333333;}#mermaid-svg-hZahhe38xEGp3V9r .marker.cross{stroke:#333333;}#mermaid-svg-hZahhe38xEGp3V9r svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-hZahhe38xEGp3V9r p{margin:0;}#mermaid-svg-hZahhe38xEGp3V9r .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-hZahhe38xEGp3V9r .cluster-label text{fill:#333;}#mermaid-svg-hZahhe38xEGp3V9r .cluster-label span{color:#333;}#mermaid-svg-hZahhe38xEGp3V9r .cluster-label span p{background-color:transparent;}#mermaid-svg-hZahhe38xEGp3V9r .label text,#mermaid-svg-hZahhe38xEGp3V9r span{fill:#333;color:#333;}#mermaid-svg-hZahhe38xEGp3V9r .node rect,#mermaid-svg-hZahhe38xEGp3V9r .node circle,#mermaid-svg-hZahhe38xEGp3V9r .node ellipse,#mermaid-svg-hZahhe38xEGp3V9r .node polygon,#mermaid-svg-hZahhe38xEGp3V9r .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-hZahhe38xEGp3V9r .rough-node .label text,#mermaid-svg-hZahhe38xEGp3V9r .node .label text,#mermaid-svg-hZahhe38xEGp3V9r .image-shape .label,#mermaid-svg-hZahhe38xEGp3V9r .icon-shape .label{text-anchor:middle;}#mermaid-svg-hZahhe38xEGp3V9r .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-hZahhe38xEGp3V9r .rough-node .label,#mermaid-svg-hZahhe38xEGp3V9r .node .label,#mermaid-svg-hZahhe38xEGp3V9r .image-shape .label,#mermaid-svg-hZahhe38xEGp3V9r .icon-shape .label{text-align:center;}#mermaid-svg-hZahhe38xEGp3V9r .node.clickable{cursor:pointer;}#mermaid-svg-hZahhe38xEGp3V9r .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-hZahhe38xEGp3V9r .arrowheadPath{fill:#333333;}#mermaid-svg-hZahhe38xEGp3V9r .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-hZahhe38xEGp3V9r .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-hZahhe38xEGp3V9r .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hZahhe38xEGp3V9r .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-hZahhe38xEGp3V9r .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hZahhe38xEGp3V9r .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-hZahhe38xEGp3V9r .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-hZahhe38xEGp3V9r .cluster text{fill:#333;}#mermaid-svg-hZahhe38xEGp3V9r .cluster span{color:#333;}#mermaid-svg-hZahhe38xEGp3V9r div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-hZahhe38xEGp3V9r .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-hZahhe38xEGp3V9r rect.text{fill:none;stroke-width:0;}#mermaid-svg-hZahhe38xEGp3V9r .icon-shape,#mermaid-svg-hZahhe38xEGp3V9r .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hZahhe38xEGp3V9r .icon-shape p,#mermaid-svg-hZahhe38xEGp3V9r .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-hZahhe38xEGp3V9r .icon-shape .label rect,#mermaid-svg-hZahhe38xEGp3V9r .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hZahhe38xEGp3V9r .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-hZahhe38xEGp3V9r .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-hZahhe38xEGp3V9r :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 客户端
反向代理
后端服务器 A
后端服务器 B
后端服务器 C
在这个过程中,客户端只知道反向代理地址,并不知道后端真实服务器是谁。
反向代理常见用途:
| 用途 | 说明 |
|---|---|
| 负载均衡 | 把请求分发到多个后端服务器 |
| 安全保护 | 隐藏后端真实地址,统一做安全策略 |
| 缓存加速 | 重复内容可以直接从代理返回 |
| 内容过滤和重写 | 修改请求头、路径或响应内容 |
| 动静分离 | 静态资源直接由代理层返回 |
| CDN | 内容分发网络常采用反向代理思想 |
反向代理的核心视角是:客户端不知道真正的后端是谁,后端服务知道前面有代理层。
八、正向代理和反向代理怎么区分
很多初学者容易把二者混在一起。可以用一句话区分:
text
正向代理代理客户端,反向代理代理服务器。
| 对比项 | 正向代理 | 反向代理 |
|---|---|---|
| 代理对象 | 客户端 | 服务端 |
| 客户端是否感知代理 | 通常知道 | 通常只访问代理入口 |
| 目标服务器看到谁 | 看到代理 | 看到反向代理转来的请求 |
| 常见用途 | 访问控制、缓存、隐藏客户端 | 负载均衡、安全隔离、缓存、动静分离 |
更通俗地说:
正向代理像"我不方便直接去买东西,让别人替我去买"。商家看到的是代买的人。
反向代理像"店家把货提前放到一个统一窗口,顾客只找窗口拿货"。顾客不需要知道后面有几个仓库。
九、内网穿透解决什么问题
NAT 的一个典型限制是:外网无法直接访问内网服务。
比如你在家里的机器上启动了一个 Web 服务:
text
192.168.1.20:8080
局域网内其他设备能访问,但公网用户无法直接访问这个私有地址。内网穿透要解决的就是:
text
让公网用户可以访问内网中的某个服务。
常见思路是引入一个公网中转节点:
#mermaid-svg-EMZBPLtX2EQIFg6d{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-EMZBPLtX2EQIFg6d .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-EMZBPLtX2EQIFg6d .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-EMZBPLtX2EQIFg6d .error-icon{fill:#552222;}#mermaid-svg-EMZBPLtX2EQIFg6d .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-EMZBPLtX2EQIFg6d .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-EMZBPLtX2EQIFg6d .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-EMZBPLtX2EQIFg6d .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-EMZBPLtX2EQIFg6d .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-EMZBPLtX2EQIFg6d .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-EMZBPLtX2EQIFg6d .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-EMZBPLtX2EQIFg6d .marker{fill:#333333;stroke:#333333;}#mermaid-svg-EMZBPLtX2EQIFg6d .marker.cross{stroke:#333333;}#mermaid-svg-EMZBPLtX2EQIFg6d svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-EMZBPLtX2EQIFg6d p{margin:0;}#mermaid-svg-EMZBPLtX2EQIFg6d .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-EMZBPLtX2EQIFg6d .cluster-label text{fill:#333;}#mermaid-svg-EMZBPLtX2EQIFg6d .cluster-label span{color:#333;}#mermaid-svg-EMZBPLtX2EQIFg6d .cluster-label span p{background-color:transparent;}#mermaid-svg-EMZBPLtX2EQIFg6d .label text,#mermaid-svg-EMZBPLtX2EQIFg6d span{fill:#333;color:#333;}#mermaid-svg-EMZBPLtX2EQIFg6d .node rect,#mermaid-svg-EMZBPLtX2EQIFg6d .node circle,#mermaid-svg-EMZBPLtX2EQIFg6d .node ellipse,#mermaid-svg-EMZBPLtX2EQIFg6d .node polygon,#mermaid-svg-EMZBPLtX2EQIFg6d .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-EMZBPLtX2EQIFg6d .rough-node .label text,#mermaid-svg-EMZBPLtX2EQIFg6d .node .label text,#mermaid-svg-EMZBPLtX2EQIFg6d .image-shape .label,#mermaid-svg-EMZBPLtX2EQIFg6d .icon-shape .label{text-anchor:middle;}#mermaid-svg-EMZBPLtX2EQIFg6d .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-EMZBPLtX2EQIFg6d .rough-node .label,#mermaid-svg-EMZBPLtX2EQIFg6d .node .label,#mermaid-svg-EMZBPLtX2EQIFg6d .image-shape .label,#mermaid-svg-EMZBPLtX2EQIFg6d .icon-shape .label{text-align:center;}#mermaid-svg-EMZBPLtX2EQIFg6d .node.clickable{cursor:pointer;}#mermaid-svg-EMZBPLtX2EQIFg6d .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-EMZBPLtX2EQIFg6d .arrowheadPath{fill:#333333;}#mermaid-svg-EMZBPLtX2EQIFg6d .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-EMZBPLtX2EQIFg6d .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-EMZBPLtX2EQIFg6d .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-EMZBPLtX2EQIFg6d .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-EMZBPLtX2EQIFg6d .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-EMZBPLtX2EQIFg6d .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-EMZBPLtX2EQIFg6d .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-EMZBPLtX2EQIFg6d .cluster text{fill:#333;}#mermaid-svg-EMZBPLtX2EQIFg6d .cluster span{color:#333;}#mermaid-svg-EMZBPLtX2EQIFg6d div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-EMZBPLtX2EQIFg6d .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-EMZBPLtX2EQIFg6d rect.text{fill:none;stroke-width:0;}#mermaid-svg-EMZBPLtX2EQIFg6d .icon-shape,#mermaid-svg-EMZBPLtX2EQIFg6d .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-EMZBPLtX2EQIFg6d .icon-shape p,#mermaid-svg-EMZBPLtX2EQIFg6d .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-EMZBPLtX2EQIFg6d .icon-shape .label rect,#mermaid-svg-EMZBPLtX2EQIFg6d .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-EMZBPLtX2EQIFg6d .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-EMZBPLtX2EQIFg6d .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-EMZBPLtX2EQIFg6d :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 公网用户
公网中转节点
内网穿透客户端
内网服务 192.168.1.20:8080
关键点在于:内网机器主动连接公网中转节点。因为这个连接是从内网主动发起的,所以可以穿过 NAT 建立出去。之后公网用户访问中转节点,中转节点再通过已经建立的连接把请求转发回内网服务。
十、内网打洞的基本理解
内网打洞可以理解为让通信双方借助公网节点交换必要信息,尝试建立可用的通信路径。
它通常需要解决几个问题:
- 内网主机没有公网地址;
- NAT 会维护映射表;
- 外部请求如果没有对应映射,通常无法直接进入;
- 需要借助公网节点让内网侧先建立可用通道。
在实际使用中,内网穿透可能采用中转模式,也可能尝试打洞直连。能不能直连,取决于 NAT 类型、网络环境和具体实现。对开发者来说,最重要的是先理解:公网访问内网服务,本质上需要一条公网可达的入口,以及一条能回到内网服务的转发通道。
十一、NAT、代理和内网穿透的关系
这三个概念都涉及"中间节点",但目的不同。
| 技术 | 主要解决的问题 | 典型位置 |
|---|---|---|
| NAT/NAPT | 多台内网主机共享公网出口 | 路由器、防火墙 |
| 正向代理 | 客户端借代理访问目标服务器 | 客户端侧或出口侧 |
| 反向代理 | 后端服务通过统一入口对外提供能力 | 服务端入口 |
| 内网穿透 | 让公网访问内网服务 | 公网中转节点 + 内网客户端 |
如果用一句话串起来:
text
NAT 让内网访问外网更容易;代理让请求转发更可控;内网穿透让外网访问内网成为可能。
十二、常见问题与易错点
1. 认为 NAT 只改 IP 不改端口
普通地址转换只解释了一部分场景。多个内网主机共享同一个公网地址时,通常还需要端口参与映射,这就是 NAPT。
2. 认为有 NAT 就能从公网访问内网服务
默认情况下,NAT 映射多由内网主动连接生成。外部主动访问内网服务时,如果没有端口映射、穿透通道或其他配置,通常无法成功。
3. 把 NAT 和代理服务器混为一谈
NAT 主要工作在网络转发层面,改地址和端口;代理服务器更接近应用层,理解请求内容,能做缓存、过滤、负载均衡等动作。
4. 分不清正向代理和反向代理
看代理的是谁:代理客户端,就是正向代理;代理服务端,就是反向代理。
5. 以为内网穿透一定是直连
内网穿透不一定意味着双方直接通信。很多实现会通过公网节点中转,是否能打洞直连取决于网络环境。
总结
NAT 是 IPv4 地址不足背景下的重要技术,它通过地址转换让大量私有地址设备共享公网出口。NAPT 进一步使用 IP 和端口建立映射,解决多台内网主机同时访问同一外部服务的问题。
代理服务和 NAT 都有"中转"味道,但代理更贴近应用层。正向代理代表客户端访问目标服务器,反向代理代表后端服务器接收客户端请求。内网穿透则是在 NAT 限制下,为公网访问内网服务建立可用入口和转发通道。