Skip to content

DNS、IP、端口与 ARP ​

S2-015建议学进阶约 60 分钟JDK 21计算机基础 · 计算机网络

1. 本节定位 ​

《HTTP 与 HTTPS》讲了"请求长什么样";本节讲**"请求怎么找到对方"**:

text
域名 → (DNS) → IP → (路由) → 目标网络 → (ARP) → 目标网卡 → 端口 → 目标进程
        ↑ 本节                      ↑ 本节

★ 本节的核心是"四种地址标识各管一段" ✓:

标识管什么范围谁在用
域名给人看的人 ✓
IP全球路由网络层 ✓
MAC局域网内链路层 ✓
端口进程传输层 ✓

2. 学习目标 ​

  • 知道 DNS 解析的完整链路与缓存层次 ★★★;
  • 知道"一台机器有多个 IP"是常态,以及它带来的问题 ★★;
  • 知道端口的三段范围与"临时端口耗尽"这个坑 ★★★;
  • 知道 ARP 的作用与 ARP 欺骗 ★★;
  • 知道 NAT 与"获取真实客户端 IP"的关系 ★★。

3. 前置知识检查 ​

4. 为什么需要这个知识 ​

先看三个由"地址标识"引发的真实事故 ✗:

事故根因
服务端 IP 变了,客户端连不上 ✗✗DNS 缓存没到期(默认可能缓存很久)
压测时大量 "Cannot assign requested address" ✗✗客户端临时端口耗尽(TIME_WAIT 堆积)
日志里的客户端 IP 全是网关地址 ✗经过 NAT/代理,服务端看到的是代理 IP

★ 三个都是"地址"的问题,但出在不同层 ✓ ——分清层次才知道该改哪里 ✓✓

5. 核心概念 ​

5.1 DNS:域名 → IP ★★★ ​

解析的完整链路 ✓:

text
浏览器缓存 → 操作系统缓存 → hosts 文件 → 本地 DNS 服务器 → 根 → 顶级域 → 权威 DNS

★ 每一层都可能命中缓存,所以"第一次慢、后面快" ✓✓

本节的实测 ✓:

text
===== 1. 域名解析(DNS)=====
  localhost        → 127.0.0.1         (101 ms)
  127.0.0.1        → 127.0.0.1         (0 ms)

===== 2. DNS 缓存:同一个域名解析 5 次 =====
  第 1 次解析:14 微秒
  第 2 次解析:10 微秒
  第 3 次解析:8 微秒
  第 4 次解析:9 微秒
  第 5 次解析:12 微秒

★ 首次 101 ms,之后几微秒 ✓✓ ——差了 4 个数量级 ✓

★ 一个"意外发现" ✓✓:

实测中 localhost 的"规范名"返回的是一个与本机无关的域名 ✓

原因是本机装过某个工具,它改写了 hosts 文件或 DNS 配置 ✓✓

这正好说明:hosts 文件的优先级高于 DNS —— 它常被用来做"本地开发时把域名指向本机",也常被恶意软件用来劫持 ⚠️

★ 三个工程要点 ✓✓:

要点说明
DNS 缓存有 TTL ★★服务端换 IP 后,客户端可能还在连老地址 ✗
JVM 有自己的 DNS 缓存 ★★★networkaddress.cache.ttl 配置 ——默认在启用安全管理器时是无限的 ⚠️
生产环境要配多个 DNS ★★单个 DNS 挂了会让服务整体不可用 ✗✗

★ 第 2 条特别容易坑人 ✓:在容器/微服务环境里,IP 是动态的 —— 所以有了"服务注册与发现"来替代"依赖 DNS + 长缓存" ✓✓

5.2 IP:一台机器有多个地址 ★★ ​

本节的实测(本机网络接口):

text
共 70 个接口,只列出有 IP 的 9 个
(含回环 127.0.0.1、局域网 10.130.12.70、并存的多个虚拟网卡)

★ 70 个接口! ✗ ——因为装了虚拟化、VPN、容器等软件,它们各建一块虚拟网卡 ✓

★ 三类地址的区别 ✓✓:

地址范围说明
127.0.0.1 ★★★本机回环不出网卡,只在本机内部 ✓
10.x / 172.16-31.x / 192.168.x ★★私有地址只在局域网内有效,出公网要 NAT ✓
公网 IP全球可达一般只有网关才有 ✓

★ 由此产生的两个实际问题 ✓✓:

问题说明
服务绑定在 127.0.0.1 时外部访问不了 ✗✗要绑 0.0.0.0 才能被外部访问(安全上要谨慎)✓
服务端拿到的客户端 IP 是"最近一跳"的地址 ⚠️经过 NAT/代理后就是代理的 IP ✓

★ 第 2 条的解法 ✓:靠代理加 X-Forwarded-For 头 —— 但它可以伪造,所以只应信任"自己已知的那一层代理"设置的值 ✓✓

5.3 端口:进程的门牌号 ★★★ ​

本节的实测(探测本机常见端口):

text
  端口       常见用途           本机状态
  22        SSH              关闭        (探测耗时 32 ms)
  80        HTTP             关闭        (探测耗时 9 ms)
  443       HTTPS            关闭        (探测耗时 9 ms)
  3306      MySQL            关闭        (探测耗时 9 ms)
  6379      Redis            关闭        (探测耗时 8 ms)
  8080      HTTP 备用         关闭        (探测耗时 8 ms)
  18090     (本项目实验用)        关闭        (探测耗时 8 ms)

★ 三段端口范围 ✓✓:

范围名称说明
0 ~ 1023 ★★知名端口需要管理员权限才能监听(80、443、22)✓
1024 ~ 49151注册端口应用常用(3306、6379、8080)✓
49152 ~ 65535 ★★★临时端口客户端发起连接时系统随便分配 ✓✓

★★ 第 3 条引出一个经典事故 ✓✓:

text
现象:压测时大量报 "Cannot assign requested address"
原因:客户端每发起一个连接都占用一个临时端口;
     连接关闭后端口进入 TIME_WAIT(默认 60 秒左右)不能立即复用;
     高 QPS 下临时端口被迅速耗尽 ✗✗

★ 三种解法 ✓:

解法说明
用连接池 ★★★根本解法:别频繁建连 ✓✓
调大临时端口范围 ★net.ipv4.ip_local_port_range ✓
开启 tcp_tw_reuse ⚠️允许复用 TIME_WAIT 的端口(要谨慎)✓

★ 这条正好呼应 《网络分层模型与数据传输过程》 的"建连很贵" ✓ —— 连接池同时解决了"握手开销"和"端口耗尽"两个问题 ✓✓

5.4 ARP:IP → MAC ★★ ​

本节的实测(arp -a):

text
    接口: 10.130.12.70 --- 0x1b
      Internet 地址            物理地址              类型
      10.130.12.1              xx-xx-xx-xx-xx-xx     动态
      ...

★ ARP 解决的问题 ✓:

text
在同一个局域网里发数据,**最终必须交给某块网卡** —— 那需要 MAC 地址 ✓
所以先广播问一句:"谁有 192.168.1.1?" → 对方回复自己的 MAC → 缓存进 ARP 表 ✓

★ 两个要点 ✓✓:

要点说明
通常只对网关做 ARP ★★跨网段的数据都先发给网关 ✓
ARP 表有超时一般几分钟到几十分钟 ✓

★ 安全引申:ARP 欺骗 ✗✗:

text
攻击者在局域网里广播:"我是网关,我的 MAC 是 XXX"
→ 所有主机的流量都发给攻击者 → 中间人 ✗✗

★ 防护 ✓:静态 ARP 绑定、交换机端口安全、动态 ARP 检测(DAI) ✓

★ 这也解释了"为什么不该随便连公共 WiFi" ✓ ——同局域网内可做的事比你想的多 ⚠️

5.5 四种标识的对照表 ★★★ ​

标识层级作用范围会变吗谁分配
域名应用层全球由你决定域名注册商 ✓
IP网络层全球路由NAT 会改写;DHCP 会变 ⚠️ISP / 云厂商 ✓
MAC链路层仅局域网每跳都可能变 ★★网卡厂商 ✓
端口传输层单台机器内客户端端口每次不同 ✓操作系统 ✓

★ 一条记忆法 ✓✓:

"域名给人看,IP 给路由器看,MAC 给交换机看,端口给操作系统看" ✓

6. 递进示例 ​

6.1 最小示例:解析与探测 ​

java
InetAddress address = InetAddress.getByName("localhost");
System.out.println(address.getHostAddress());          // 127.0.0.1

try (Socket socket = new Socket()) {
    socket.connect(new InetSocketAddress("127.0.0.1", 8080), 300);   // ★ 带超时!
    System.out.println("端口开放");
} catch (IOException e) {
    System.out.println("端口关闭或超时");
}

★ 一定要带超时 ✗ ——否则探测一个被防火墙丢弃的端口会挂很久 ✓

6.2 业务示例:枚举本机地址 ​

java
NetworkInterface.getNetworkInterfaces().asIterator().forEachRemaining(nic -> {
    // 只关心有地址且 up 的接口
});

6.3 反例:把客户端 IP 当作"可信身份" ​

java
// ✗ 用 X-Forwarded-For 做鉴权
String clientIp = request.getHeader("X-Forwarded-For");
if (clientIp.startsWith("10.0.")) { allowInternalApi(); }    // ✗ 可伪造!

★ 正确做法 ✓:用"只信任第 N 层代理设置的 XFF"策略,或者干脆用认证令牌 ✓✓

7. 完整可运行代码与实测 ​

代码:labs/cs-basics/src/main/java/cn/kb/learn/cs/net/DnsIpPortLesson.java

运行:

powershell
cd labs
.\mvnw.cmd -pl cs-basics compile
java "-Dstdout.encoding=UTF-8" -cp cs-basics/target/classes cn.kb.learn.cs.net.DnsIpPortLesson

实测输出(节选):

text
===== 2. DNS 缓存:同一个域名解析 5 次 =====
  第 1 次解析:14 微秒      ← 系统已有缓存
  第 5 次解析:12 微秒

===== 3. 本机网络接口与地址 =====
  共 70 个接口,只列出有 IP 的 9 个
  loopback_0      状态=up  MAC=无  地址=0:0:0:0:0:0:0:1, 127.0.0.1

===== 4. 端口探测(本机常见端口)=====
  22   SSH   关闭(探测耗时 32 ms)
  80   HTTP  关闭(探测耗时  9 ms)

★ 三条可落地的结论 ✓:

#结论依据
①客户端要处理"DNS 缓存导致连老地址"解析有 TTL,JVM 还有自己的缓存 ✓
②高并发客户端必须用连接池否则临时端口耗尽 ✗
③客户端 IP 只能当"参考",不能当"身份"经过 NAT/代理且头可伪造 ✓

8. Debug 观察点 ​

  1. 用 nslookup / Resolve-DnsName 解析同一个域名两次:观察第二次更快 ✓;
  2. 用 netstat -ano | findstr TIME_WAIT:观察连接关闭后的状态堆积 ★★;
  3. 用 netstat -ano | findstr LISTENING:看本机在监听哪些端口 ✓;
  4. ping 一个不存在的内网 IP:观察 ARP 失败与超时 ✓;
  5. 用 ipconfig /displaydns(Windows)看 DNS 缓存内容 ★。

9. 常见错误 ​

#错误说明
1忽略 DNS 缓存导致连老 IP ✗✗容器/云环境尤其常见 ✓
2服务只绑 127.0.0.1 ✗✗外部访问不了 ✓
3高并发下频繁建连 ✗✗临时端口耗尽 ✓
4端口探测不带超时 ✗会挂很久 ✓
5把客户端 IP 当鉴权依据 ✗✗NAT + 可伪造 ✓✓
6以为 MAC 能跨网段标识主机 ✗✗每跳都会换 ✓
7以为"内网就安全" ✗✗ARP 欺骗、同网段嗅探 ✓

10. 动手实验 ​

目标:亲手验证"四种标识各管一段"。

步骤:

  1. 跑 DnsIpPortLesson,记录 DNS 首次与缓存后的差异 ✓;
  2. 用 ipconfig /displaydns 看缓存条目,再 ipconfig /flushdns 清空,对比解析耗时 ★★;
  3. 起一个服务只绑 127.0.0.1,从另一台机器/另一个网卡地址访问,观察连不上 ★;
  4. 用 netstat 观察 TIME_WAIT 累积(跑一段高频短连接)★★;
  5. arp -a 前后对比:先 ping 一个内网地址,再看 ARP 表多了什么 ✓。

验收方式:能说出"客户端的临时端口范围是多少、为什么会耗尽"。

扩展任务:写一个"带退避的 DNS 轮询":

  • 对同一个域名反复解析,记录返回的 IP 列表(多 A 记录时会轮询)✓;
  • 实现"解析结果本地缓存 + TTL 到期刷新" ✓;
  • 说明:这就是"客户端负载均衡"的最小形态(比 DNS 轮询更可控)★★

11. 练习题 ​

  1. 判断:一台机器只有一个 IP 地址。(对 / 错)
  2. 填空:DNS 的作用是 ______;ARP 的作用是 ______;端口的第 3 段范围是 ______。
  3. 简答:为什么压测时会出现 "Cannot assign requested address"?
  4. (代码阅读)
java
String ip = request.getHeader("X-Forwarded-For");
if ("127.0.0.1".equals(ip)) { return adminData(); }

这段代码的安全问题是什么? 5. (编程)写一个"端口探测器":扫描 127.0.0.1 的 1~1024 端口,每个 200 ms 超时,输出开放的端口。

参考答案(要点)
  1. 错。本机实测有 9 个有 IP 的接口(含虚拟网卡)✓★
  2. 域名 → IP;IP → MAC(同一局域网内);临时端口(49152~65535) ✓
  3. 客户端每建一个连接就占一个临时端口,关闭后进入 TIME_WAIT 不能立即复用 ✗ —— 高 QPS 下被耗尽 ✓★(解法:连接池)
  4. X-Forwarded-For 可以被客户端伪造 ✗✗ ——任何人都能自称 127.0.0.1 拿到管理员数据 ✓
  5. 要点:必须带超时(否则被防火墙丢弃的端口会挂很久) ✓ 注意:部分系统对连接数有限制,扫描时要控制并发 ✓

12. 掌握标准 ​

  • [ ] 能说出 DNS 解析的完整链路与缓存层次 ★★★;
  • [ ] 知道"一台机器多个 IP"是常态,以及绑定地址的坑 ★★;
  • [ ] 知道端口的三段范围与"临时端口耗尽" ★★★;
  • [ ] 知道 ARP 的作用范围与 ARP 欺骗 ★★;
  • [ ] 知道 NAT 让"客户端 IP"不可信 ★★;
  • [ ] 能背出四种标识各管什么范围 ★★★。

13. 扩展资源 ​

14. 下一步学习 ​

《网络安全基础》:当数据走在不可信网络里,怎么保护它。

进度只保存在本机浏览器,可随时清除

个人学习项目:AI 生成内容均经人工复核,第三方资源仅作索引