主题
界面
主题
《HTTP 与 HTTPS》讲了"请求长什么样";本节讲**"请求怎么找到对方"**:
域名 → (DNS) → IP → (路由) → 目标网络 → (ARP) → 目标网卡 → 端口 → 目标进程
↑ 本节 ↑ 本节★ 本节的核心是"四种地址标识各管一段" ✓:
| 标识 | 管什么范围 | 谁在用 |
|---|---|---|
| 域名 | 给人看的 | 人 ✓ |
| IP | 全球路由 | 网络层 ✓ |
| MAC | 局域网内 | 链路层 ✓ |
| 端口 | 进程 | 传输层 ✓ |
ping / nslookup 吗?✓先看三个由"地址标识"引发的真实事故 ✗:
| 事故 | 根因 |
|---|---|
| 服务端 IP 变了,客户端连不上 ✗✗ | DNS 缓存没到期(默认可能缓存很久) |
| 压测时大量 "Cannot assign requested address" ✗✗ | 客户端临时端口耗尽(TIME_WAIT 堆积) |
| 日志里的客户端 IP 全是网关地址 ✗ | 经过 NAT/代理,服务端看到的是代理 IP |
★ 三个都是"地址"的问题,但出在不同层 ✓ ——分清层次才知道该改哪里 ✓✓
解析的完整链路 ✓:
浏览器缓存 → 操作系统缓存 → hosts 文件 → 本地 DNS 服务器 → 根 → 顶级域 → 权威 DNS★ 每一层都可能命中缓存,所以"第一次慢、后面快" ✓✓
本节的实测 ✓:
===== 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 + 长缓存" ✓✓
本节的实测(本机网络接口):
共 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 头 —— 但它可以伪造,所以只应信任"自己已知的那一层代理"设置的值 ✓✓
本节的实测(探测本机常见端口):
端口 常见用途 本机状态
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 条引出一个经典事故 ✓✓:
现象:压测时大量报 "Cannot assign requested address"
原因:客户端每发起一个连接都占用一个临时端口;
连接关闭后端口进入 TIME_WAIT(默认 60 秒左右)不能立即复用;
高 QPS 下临时端口被迅速耗尽 ✗✗★ 三种解法 ✓:
| 解法 | 说明 |
|---|---|
| 用连接池 ★★★ | 根本解法:别频繁建连 ✓✓ |
| 调大临时端口范围 ★ | net.ipv4.ip_local_port_range ✓ |
开启 tcp_tw_reuse ⚠️ | 允许复用 TIME_WAIT 的端口(要谨慎)✓ |
★ 这条正好呼应 《网络分层模型与数据传输过程》 的"建连很贵" ✓ —— 连接池同时解决了"握手开销"和"端口耗尽"两个问题 ✓✓
本节的实测(arp -a):
接口: 10.130.12.70 --- 0x1b
Internet 地址 物理地址 类型
10.130.12.1 xx-xx-xx-xx-xx-xx 动态
...★ ARP 解决的问题 ✓:
在同一个局域网里发数据,**最终必须交给某块网卡** —— 那需要 MAC 地址 ✓
所以先广播问一句:"谁有 192.168.1.1?" → 对方回复自己的 MAC → 缓存进 ARP 表 ✓★ 两个要点 ✓✓:
| 要点 | 说明 |
|---|---|
| 通常只对网关做 ARP ★★ | 跨网段的数据都先发给网关 ✓ |
| ARP 表有超时 | 一般几分钟到几十分钟 ✓ |
★ 安全引申:ARP 欺骗 ✗✗:
攻击者在局域网里广播:"我是网关,我的 MAC 是 XXX"
→ 所有主机的流量都发给攻击者 → 中间人 ✗✗★ 防护 ✓:静态 ARP 绑定、交换机端口安全、动态 ARP 检测(DAI) ✓
★ 这也解释了"为什么不该随便连公共 WiFi" ✓ ——同局域网内可做的事比你想的多 ⚠️
| 标识 | 层级 | 作用范围 | 会变吗 | 谁分配 |
|---|---|---|---|---|
| 域名 | 应用层 | 全球 | 由你决定 | 域名注册商 ✓ |
| IP | 网络层 | 全球路由 | NAT 会改写;DHCP 会变 ⚠️ | ISP / 云厂商 ✓ |
| MAC | 链路层 | 仅局域网 | 每跳都可能变 ★★ | 网卡厂商 ✓ |
| 端口 | 传输层 | 单台机器内 | 客户端端口每次不同 ✓ | 操作系统 ✓ |
★ 一条记忆法 ✓✓:
"域名给人看,IP 给路由器看,MAC 给交换机看,端口给操作系统看" ✓
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("端口关闭或超时");
}★ 一定要带超时 ✗ ——否则探测一个被防火墙丢弃的端口会挂很久 ✓
NetworkInterface.getNetworkInterfaces().asIterator().forEachRemaining(nic -> {
// 只关心有地址且 up 的接口
});// ✗ 用 X-Forwarded-For 做鉴权
String clientIp = request.getHeader("X-Forwarded-For");
if (clientIp.startsWith("10.0.")) { allowInternalApi(); } // ✗ 可伪造!★ 正确做法 ✓:用"只信任第 N 层代理设置的 XFF"策略,或者干脆用认证令牌 ✓✓
代码:labs/cs-basics/src/main/java/cn/kb/learn/cs/net/DnsIpPortLesson.java
运行:
cd labs
.\mvnw.cmd -pl cs-basics compile
java "-Dstdout.encoding=UTF-8" -cp cs-basics/target/classes cn.kb.learn.cs.net.DnsIpPortLesson实测输出(节选):
===== 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/代理且头可伪造 ✓ |
nslookup / Resolve-DnsName 解析同一个域名两次:观察第二次更快 ✓;netstat -ano | findstr TIME_WAIT:观察连接关闭后的状态堆积 ★★;netstat -ano | findstr LISTENING:看本机在监听哪些端口 ✓;ping 一个不存在的内网 IP:观察 ARP 失败与超时 ✓;ipconfig /displaydns(Windows)看 DNS 缓存内容 ★。| # | 错误 | 说明 |
|---|---|---|
| 1 | 忽略 DNS 缓存导致连老 IP ✗✗ | 容器/云环境尤其常见 ✓ |
| 2 | 服务只绑 127.0.0.1 ✗✗ | 外部访问不了 ✓ |
| 3 | 高并发下频繁建连 ✗✗ | 临时端口耗尽 ✓ |
| 4 | 端口探测不带超时 ✗ | 会挂很久 ✓ |
| 5 | 把客户端 IP 当鉴权依据 ✗✗ | NAT + 可伪造 ✓✓ |
| 6 | 以为 MAC 能跨网段标识主机 ✗✗ | 每跳都会换 ✓ |
| 7 | 以为"内网就安全" ✗✗ | ARP 欺骗、同网段嗅探 ✓ |
目标:亲手验证"四种标识各管一段"。
步骤:
DnsIpPortLesson,记录 DNS 首次与缓存后的差异 ✓;ipconfig /displaydns 看缓存条目,再 ipconfig /flushdns 清空,对比解析耗时 ★★;127.0.0.1,从另一台机器/另一个网卡地址访问,观察连不上 ★;netstat 观察 TIME_WAIT 累积(跑一段高频短连接)★★;arp -a 前后对比:先 ping 一个内网地址,再看 ARP 表多了什么 ✓。验收方式:能说出"客户端的临时端口范围是多少、为什么会耗尽"。
扩展任务:写一个"带退避的 DNS 轮询":
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 超时,输出开放的端口。
X-Forwarded-For 可以被客户端伪造 ✗✗ ——任何人都能自称 127.0.0.1 拿到管理员数据 ✓《网络安全基础》:当数据走在不可信网络里,怎么保护它。
暂无