1
0
mirror of https://github.com/Snailclimb/JavaGuide synced 2025-06-16 18:10:13 +08:00

[docs add]NAT 协议详解(网络层)

This commit is contained in:
Guide 2023-04-30 16:44:12 +08:00
parent 1e70a902ee
commit 0fa55ed949
9 changed files with 94 additions and 8 deletions

View File

@ -150,6 +150,7 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8 ](https://docs.oracle
- [TCP 三次握手和四次挥手(传输层)](./docs/cs-basics/network/tcp-connection-and-disconnection.md)
- [TCP 传输可靠性保障(传输层)](./docs/cs-basics/network/tcp-reliability-guarantee.md)
- [ARP 协议详解(网络层)](./docs/cs-basics/network/arp.md)
- [NAT 协议详解(网络层)](./docs/cs-basics/network/nat.md)
- [网络攻击常见手段总结(安全)](./docs/cs-basics/network/network-attack-means.md)
### 数据结构

View File

@ -184,6 +184,7 @@ export default sidebar({
"tcp-connection-and-disconnection",
"tcp-reliability-guarantee",
"arp",
"nat",
"network-attack-means",
],
},

View File

@ -1,3 +1,3 @@
$theme-color: #2980b9;
// $sidebar-width: 20rem;
// $sidebar-mobile-width: 16rem;
$sidebar-width: 20rem;
$sidebar-mobile-width: 16rem;

View File

@ -19,7 +19,9 @@ export default hopeTheme({
repo: "https://github.com/Snailclimb/JavaGuide",
docsDir: "docs",
// 纯净模式https://theme-hope.vuejs.press/zh/guide/interface/pure.html
pure: true,
breadcrumb: false,
navbar,
sidebar,
footer:

View File

@ -0,0 +1,56 @@
---
title: NAT 协议详解(网络层)
category: 计算机基础
tag:
- 计算机网络
---
## 应用场景
**NAT 协议Network Address Translation** 的应用场景如同它的名称——网络地址转换应用于内部网到外部网的地址转换过程中。具体地说在一个小的子网局域网LAN各主机使用的是同一个 LAN 下的 IP 地址,但在该 LAN 以外在广域网WAN需要一个统一的 IP 地址来标识该 LAN 在整个 Internet 上的位置。
这个场景其实不难理解。随着一个个小型办公室、家庭办公室Small Office, Home Office, SOHO的出现为了管理这些 SOHO一个个子网被设计出来从而在整个 Internet 中的主机数量将非常庞大。如果每个主机都有一个“绝对唯一”的 IP 地址,那么 IPv4 地址的表达能力可能很快达到上限($2^{32}$。因此实际上SOHO 子网中的 IP 地址是“相对的”,这在一定程度上也缓解了 IPv4 地址的分配压力。
SOHO 子网的“代理人”,也就是和外界的窗口,通常由路由器扮演。路由器的 LAN 一侧管理着一个小子网,而它的 WAN 接口才是真正参与到 Internet 中的接口也就有一个“绝对唯一的地址”。NAT 协议,正是在 LAN 中的主机在与 LAN 外界通信时,起到了地址转换的关键作用。
## 细节
![NAT 协议](https://oss.javaguide.cn/github/javaguide/cs-basics/network/nat-demo.png)
假设当前场景如上图。中间是一个路由器,它的右侧组织了一个 WAN该局域网的网络号为`10.0.0/24`WAN 侧接口的 IP 地址为`10.0.0.4`,并且该子网内有至少三台主机,分别是`10.0.0.1``10.0.0.2``10.0.0.3`。路由器的左侧连接的是 WANWAN 侧接口的 IP 地址为`138.76.29.7`
首先,针对以上信息,我们有如下事实需要说明:
1. 路由器的右侧子网的网络号为`10.0.0/24`,主机号为`10.0.0/8`,三台主机地址,以及路由器的 LAN 侧接口地址,均由 DHCP 协议规定。而且,该 DHCP 运行在路由器内部(路由器自维护一个小 DHCP 服务器),从而为子网内提供 DHCP 服务。
2. 路由器的 WAN 侧接口地址同样由 DHCP 协议规定,但该地址是路由器从 ISP网络服务提供商处获得也就是该 DHCP 通常运行在路由器所在区域的 DHCP 服务器上。
现在,路由器内部还运行着 NAT 协议,从而为 LAN-WAN 间通信提供地址转换服务。为此,一个很重要的结构是 **NAT 转换表**。为了说明 NAT 的运行细节,假设有以下请求发生:
1. 主机`10.0.0.1`向 IP 地址为`128.119.40.186`的 Web 服务器(端口 80发送了 HTTP 请求(如请求页面)。此时,主机`10.0.0.1`将随机指派一个端口,如`3345`,作为本次请求的源端口号,将该请求发送到路由器中(目的地址将是`128.119.40.186`,但会先到达`10.0.0.4`)。
2. `10.0.0.4`即路由器的 LAN 接口收到`10.0.0.1`的请求。路由器将为该请求指派一个新的源端口号,如`5001`,并将请求报文发送给 WAN 接口`138.76.29.7`。同时,在 NAT 转换表中记录一条转换记录**138.76.29.7:5001——10.0.0.1:3345**。
3. 请求报文到达 WAN 接口,继续向目的主机`128.119.40.186`发送。
之后,将会有如下响应发生:
1. 主机`128.119.40.186`收到请求,构造响应报文,并将其发送给目的地`138.76.29.7:5001`
2. 响应报文到达路由器的 WAN 接口。路由器查询 NAT 转换表,发现`138.76.29.7:5001`在转换表中有记录,从而将其目的地址和目的端口转换成为`10.0.0.1:3345`,再发送到`10.0.0.4`上。
3. 被转换的响应报文到达路由器的 LAN 接口,继而被转发至目的地`10.0.0.1`
![LAN-WAN 间通信提供地址转换](https://oss.javaguide.cn/github/javaguide/cs-basics/network/nat-demo2.png)
## 划重点
针对以上过程,有以下几个重点需要强调:
1. 当请求报文到达路由器,并被指定了新端口号时,由于端口号有 16 位,因此,通常来说,一个路由器管理的 LAN 中的最大主机数 $≈65500$$2^{16}$ 的地址空间),但通常 SOHO 子网内不会有如此多的主机数量。
2. 对于目的服务器来说,从来不知道“到底是哪个主机给我发送的请求”,它只知道是来自`138.76.29.7:5001`的路由器转发的请求。因此,可以说,**路由器在 WAN 和 LAN 之间起到了屏蔽作用,**所有内部主机发送到外部的报文,都具有同一个 IP 地址(不同的端口号),所有外部发送到内部的报文,也都只有一个目的地(不同端口号),是经过了 NAT 转换后,外部报文才得以正确地送达内部主机。
3. 在报文穿过路由器,发生 NAT 转换时,如果 LAN 主机 IP 已经在 NAT 转换表中注册过了,则不需要路由器新指派端口,而是直接按照转换记录穿过路由器。同理,外部报文发送至内部时也如此。
总结 NAT 协议的特点,有以下几点:
1. NAT 协议通过对 WAN 屏蔽 LAN有效地缓解了 IPv4 地址分配压力。
2. LAN 主机 IP 地址的变更,无需通告 WAN。
3. WAN 的 ISP 变更接口地址时,无需通告 LAN 内主机。
4. LAN 主机对 WAN 不可见,不可直接寻址,可以保证一定程度的安全性。
然而NAT 协议由于其独特性,存在着一些争议。比如,可能你已经注意到了,**NAT 协议在 LAN 以外,标识一个内部主机时,使用的是端口号,因为 IP 地址都是相同的。**这种将端口号作为主机寻址的行为,可能会引发一些误会。此外,路由器作为网络层的设备,修改了传输层的分组内容(修改了源 IP 地址和端口号同样是不规范的行为。但是尽管如此NAT 协议作为 IPv4 时代的产物,极大地方便了一些本来棘手的问题,一直被沿用至今。

View File

@ -17,7 +17,7 @@ tag:
- **二次握手**:服务端发送带有 SYN+ACK(SEQ=y,ACK=x+1) 标志的数据包 > 客户端,然后服务端进入 **SYN_RECV** 状态
- **三次握手**:客户端发送带有 ACK(ACK=y+1) 标志的数据包 > 服务端,然后客户端和服务器端都进入**ESTABLISHED** 状态,完成 TCP 三次握手。
**当建立了 3 次握手之后,客户端和服务端就可以传输数据啦!**
当建立了 3 次握手之后,客户端和服务端就可以传输数据啦!
### 为什么要三次握手?
@ -43,10 +43,10 @@ tag:
断开一个 TCP 连接则需要“四次挥手”,缺一不可
1. **第一次挥手** :客户端发送一个 FINSEQ=X 标志的数据包->服务端,用来关闭客户端到服务器的数据传送。然后,客户端进入 **FIN-WAIT-1** 状态。
2. **第二次挥手** :服务器收到这个 FINSEQ=X 标志的数据包,它发送一个 ACK SEQ=X+1标志的数据包->客户端 。然后,此时服务端进入**CLOSE-WAIT**状态,客户端进入**FIN-WAIT-2**状态。
3. **第三次挥手** :服务端关闭与客户端的连接并发送一个 FIN (SEQ=y)标志的数据包->客户端请求关闭连接,然后,服务端进入**LAST-ACK**状态。
4. **第四次挥手** :客户端发送 ACK (SEQ=y+1)标志的数据包->服务端并且进入**TIME-WAIT**状态,服务端在收到 ACK (SEQ=y+1)标志的数据包后进入 CLOSE 状态。此时,如果客户端等待 **2MSL** 后依然没有收到回复,就证明服务端已正常关闭,随后,客户端也可以关闭连接了。
1. **第一次挥手** :客户端发送一个 FINSEQ=x 标志的数据包->服务端,用来关闭客户端到服务器的数据传送。然后,客户端进入 **FIN-WAIT-1** 状态。
2. **第二次挥手** :服务器收到这个 FINSEQ=X 标志的数据包,它发送一个 ACK ACK=x+1标志的数据包->客户端 。然后,此时服务端进入 **CLOSE-WAIT** 状态,客户端进入 **FIN-WAIT-2** 状态。
3. **第三次挥手** :服务端关闭与客户端的连接并发送一个 FIN (SEQ=y)标志的数据包->客户端请求关闭连接,然后,服务端进入 **LAST-ACK** 状态。
4. **第四次挥手** :客户端发送 ACK (ACK=y+1)标志的数据包->服务端并且进入**TIME-WAIT**状态,服务端在收到 ACK (ACK=y+1)标志的数据包后进入 CLOSE 状态。此时,如果客户端等待 **2MSL** 后依然没有收到回复,就证明服务端已正常关闭,随后,客户端也可以关闭连接了。
**只要四次挥手没有结束,客户端和服务端就可以继续传输数据!**

View File

@ -6,6 +6,7 @@ tag:
---
**缓存基础** 相关的面试题为我的[知识星球](https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html)(点击链接即可查看详细介绍以及加入方法)专属内容,已经整理到了[《Java 面试指北》](https://javaguide.cn/zhuanlan/java-mian-shi-zhi-bei.html)中。
![](https://oss.javaguide.cn/javamianshizhibei/database-questions.png)
<!-- @include: @planet.snippet.md -->

View File

@ -147,6 +147,7 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8](https://docs.oracle.
- [TCP 三次握手和四次挥手(传输层)](./cs-basics/network/tcp-connection-and-disconnection.md)
- [TCP 传输可靠性保障(传输层)](./cs-basics/network/tcp-reliability-guarantee.md)
- [ARP 协议详解(网络层)](./cs-basics/network/arp.md)
- [NAT 协议详解(网络层)](./docs/cs-basics/network/nat.md)
- [网络攻击常见手段总结(安全)](./cs-basics/network/network-attack-means.md)
### 数据结构

View File

@ -290,6 +290,30 @@ final void treeifyBin(Node<K,V>[] tab, int hash) {
[HashMap 的 7 种遍历方式与性能分析!](https://mp.weixin.qq.com/s/zQBN3UvJDhRTKP6SzcZFKw)
**🐛 修正(参见: [issue#1411](https://github.com/Snailclimb/JavaGuide/issues/1411)**
这篇文章对于 parallelStream 遍历方式的性能分析有误,先说结论: **存在阻塞时 parallelStream 性能最高, 非阻塞时 parallelStream 性能最低**
当遍历不存在阻塞时, parallelStream 的性能是最低的:
```
Benchmark Mode Cnt Score Error Units
Test.entrySet avgt 5 288.651 ± 10.536 ns/op
Test.keySet avgt 5 584.594 ± 21.431 ns/op
Test.lambda avgt 5 221.791 ± 10.198 ns/op
Test.parallelStream avgt 5 6919.163 ± 1116.139 ns/op
```
加入阻塞代码`Thread.sleep(10)`后, parallelStream 的性能才是最高的:
```
Benchmark Mode Cnt Score Error Units
Test.entrySet avgt 5 1554828440.000 ± 23657748.653 ns/op
Test.keySet avgt 5 1550612500.000 ± 6474562.858 ns/op
Test.lambda avgt 5 1551065180.000 ± 19164407.426 ns/op
Test.parallelStream avgt 5 186345456.667 ± 3210435.590 ns/op
```
### ConcurrentHashMap 和 Hashtable 的区别
`ConcurrentHashMap``Hashtable` 的区别主要体现在实现线程安全的方式上不同。