一、域名解析到服务器的过程(DNS 解析流程)
当应用发起网络请求(如https://www.example.com
)时,操作系统需先将域名转换为服务器 IP 地址,这一过程通过 DNS(域名系统) 完成,具体步骤如下:
1. 客户端本地缓存查询(递归查询)
- 浏览器缓存:浏览器会首先检查自身缓存(如 Chrome 通过
chrome://net-internals/#dns
查看),存储近期解析过的域名 - IP 映射,有效期由 DNS 响应的TTL
(生存时间)决定。 - 系统缓存:若浏览器未命中,操作系统(Android)会查询本地 DNS 缓存(可通过
android.system.DnsCache
类访问,但需权限)。 - Hosts 文件:Android 系统会读取
/system/etc/hosts
文件(需 root 权限)或应用自定义的 hosts 文件(如通过 VPN 代理设置),优先于 DNS 服务器查询。
2. DNS 服务器迭代查询
若本地缓存未命中,客户端会向 本地 DNS 服务器(如运营商提供的 DNS,或用户手动设置的公共 DNS 如 8.8.8.8)发起查询,该过程为 迭代查询:
- 根域名服务器:本地 DNS 服务器首先查询根域名服务器(全球 13 组,负责
.com
、.cn
等顶级域名),根服务器返回对应顶级域名服务器(TLD Server)的地址。 - 顶级域名服务器:如
.com
服务器,返回域名example.com
的权威域名服务器(Authoritative Server)地址。 - 权威域名服务器:存储
example.com
的 DNS 记录(如 A 记录、AAAA 记录),返回最终的 IP 地址(如192.168.1.1
)。
3. 返回 IP 地址
本地 DNS 服务器将解析结果缓存并返回给客户端,后续请求可直接使用 IP 地址建立连接。
二、DNS 解析过程的核心机制
1. 递归查询 vs 迭代查询
- 递归查询(客户端→本地 DNS 服务器):客户端只需发送一次查询请求,本地 DNS 服务器负责全程查询并返回结果(类似 “一站式服务”)。
- 迭代查询(本地 DNS 服务器→根→TLD→权威服务器):每一步查询返回下一级服务器地址,需多次交互(类似 “分步导航”)。
2. DNS 记录类型
- A 记录:域名→IPv4 地址(最常用)。
- AAAA 记录:域名→IPv6 地址。
- CNAME 记录:别名解析(如
www.example.com
→example.com
)。 - MX 记录:邮件服务器解析。
- TXT 记录:存储文本信息(如 SPF 反垃圾邮件策略)。
三、DNS 请求使用的协议:UDP 为主,TCP 为辅
1. 默认使用 UDP(端口 53)
- 原因:
- 轻量化:DNS 查询 / 响应数据量小(通常 < 512 字节),UDP 无需建立连接,延迟更低(适合移动端弱网场景)。
- 速度优先:移动端网络环境复杂,UDP 的无连接特性避免了 TCP 三次握手的耗时。
- Android 中的实现:
- 系统通过
Dns
类(如Dns.resolve()
)发起 UDP 查询,底层调用libresolv
库。
- 系统通过
2. 何时使用 TCP?
- DNS 区域传输:主从 DNS 服务器同步数据时,因数据量大(可能超过 UDP 缓冲区),使用 TCP 保证可靠传输。
- 响应超过 512 字节:当 DNS 响应数据超过 UDP 最大载荷(512 字节),服务器会在响应中标记
TC
(Truncated)标志,客户端自动切换为 TCP 重新查询。
四、UDP 不可靠性的解决方案
虽然 UDP 不保证可靠传输,但 DNS 通过以下机制弥补缺陷:
1. 客户端超时重传
- 客户端发送 UDP 查询后,若未在超时时间(通常 2-5 秒)内收到响应,会自动重试(默认重试次数 2-3 次)。
- Android 源码中,
DnsResolver
类通过AsyncTask
实现异步查询,超时后触发重试逻辑。
2. DNS 服务器的可靠性设计
- 冗余部署:权威 DNS 服务器通常部署多副本,避免单点故障(如阿里云 DNS、Cloudflare DNS)。
- TCP 作为备用:当 UDP 查询失败(如频繁超时),客户端会尝试通过 TCP 重新请求(如 HTTPS 场景下的 DoH(DNS over HTTPS)强制使用 TCP)。
3. 移动端优化
- 预解析:在应用启动或空闲时,提前解析常用域名(如通过
android:usesCleartextTraffic
配置或DnsPrefetch
策略),减少实时查询延迟。 - HTTPS 化:使用 DoH(RFC 8484)或 DoT(DNS over TLS),通过 HTTPS/TLS 加密 DNS 流量,避免 UDP 被劫持,同时利用 TCP 的可靠性。
五、TCP 滑动窗口:流量控制与高效传输
1. 核心作用
滑动窗口是 TCP 实现 流量控制 和 拥塞控制 的关键机制,允许发送方在收到确认前批量发送数据,提升传输效率。
2. 工作原理
- 窗口大小:接收方在 ACK 报文中告知发送方自己当前的接收缓冲区剩余空间(即 “接收窗口”),发送方根据此动态调整 “发送窗口” 大小。
- 滑动过程:
- 发送方将数据分割为多个段(Segment),放入发送窗口,无需等待每个段的 ACK 即可连续发送。
- 接收方收到数据后,返回 ACK 并更新接收窗口(如
ACK 1000 + 窗口大小2000
表示 “前 1000 字节已接收,剩余可接收 2000 字节”)。 - 发送方根据 ACK 移动窗口,发送窗口右侧未确认的数据会触发超时重传。
3. 滑动窗口的三个阶段(结合 Android 网络优化)
- 慢启动(Slow Start):初始窗口较小(如 1-2 个 MSS),逐步指数级增长,避免网络拥塞(如 Android 下载大文件时的初始阶段)。
- 拥塞避免(Congestion Avoidance):窗口增长到阈值后,转为线性增长,防止网络过载(如 OkHttp 的连接池管理)。
- 快速重传与恢复(Fast Retransmit/Fast Recovery):若收到 3 个重复 ACK,判定数据段丢失,立即重传而不等待超时(提升弱网环境下的稳定性)。
4. Android 中的实际应用
- OkHttp 连接优化:通过
ConnectionSpec
配置 TCP 参数(如窗口大小、超时时间),结合滑动窗口机制减少延迟。 - NDK 开发:在 C/C++ 层通过
setsockopt()
设置 TCP 窗口选项(如TCP_NODELAY
禁用 Nagle 算法,配合滑动窗口提升实时数据传输效率)。
总结(面试应答要点)
- DNS 解析流程:从本地缓存到递归 / 迭代查询,逐层解析域名到 IP,结合 TTL 缓存提升性能。
- DNS 协议选择:默认 UDP(低延迟),必要时切换 TCP(大数据量 / 可靠性),移动端可通过预解析和 DoH 优化。
- UDP 可靠性:依赖客户端超时重传、服务器冗余及 TCP 备用通道,结合现代加密方案(DoH/DoT)增强安全性。
- TCP 滑动窗口:通过动态调整发送 / 接收窗口实现流量控制,分阶段优化传输效率,是 HTTP/2 多路复用、QUIC 协议的底层基础。
扩展追问:
一、DNS 挟持的核心原理(面试必问基础)
1. 基础概念与攻击本质
DNS 挟持是通过篡改域名解析结果,将用户请求的域名(如www.bank.com
)指向恶意 IP 地址,导致用户访问钓鱼网站或被劫持流量的攻击行为。其核心逻辑是破坏域名到 IP 的映射关系,分为以下三个阶段:
- 请求阶段:用户设备向 DNS 服务器发送域名解析请求(如
nslookup www.example.com
)。 - 篡改阶段:攻击者通过中间人攻击、路由器漏洞或恶意软件,在 DNS 查询路径中替换正确的 IP 地址为恶意地址。
- 重定向阶段:用户设备根据伪造的 IP 连接到恶意服务器,导致数据泄露或服务中断。
2. Android 特有的攻击场景
- 恶意 APP 篡改本地配置:
攻击者通过WRITE_SETTINGS
权限修改设备 DNS 服务器地址,或篡改/system/etc/hosts
文件(需 root 权限)15。// 示例:恶意APP修改DNS设置(需权限) Settings.Global.putString(context.getContentResolver(), Settings.Global.ETH0_DNS1, "192.168.1.253"); // 篡改DNS服务器
- 公共 Wi-Fi 中间人攻击:
攻击者在咖啡店等场景伪造热点,通过 DNS 欺骗将用户流量导向钓鱼页面14。 - 运营商层面劫持:
部分 ISP 违规插入广告页面(如访问不存在的域名时跳转广告),或篡改 DNS 响应2。
二、Android 系统的 DNS 解析流程(面试重点:技术实现)
1. 解析流程与关键组件
Android 设备的 DNS 解析分为系统层和应用层两个层面:
- 系统层解析:
- 设备向本地 DNS 服务器(如运营商 DNS)发送 UDP/TCP 请求。
- DNS 服务器递归查询根域名服务器,获取目标 IP 地址。
- 系统缓存解析结果(TTL 时间内复用)。
- 应用层解析:
- 应用通过
InetAddress.getByName()
或网络库(如 OkHttp)发起解析。 - 若系统 DNS 被劫持,解析结果可能指向恶意 IP34。
- 应用通过
2. Android 版本差异与限制
- Android 7.0(API 24)限制:
禁止普通应用直接修改全局 DNS 设置,需通过 VPN 或设备管理员权限配置21。 - Android 9+(API 28)支持 DoT:
可通过系统设置或代码启用加密 DNS(如DnsOverTls
),防止中间人篡改910。 - Android 10+(API 29)支持 DoH:
部分浏览器(如 Chrome)默认启用 DNS over HTTPS,加密解析过程27。
三、DNS 挟持的检测方法(面试高频:代码实现)
1. 编程层面检测
- 对比官方 IP 地址:
try { InetAddress address = InetAddress.getByName("www.google.com"); String expectedIp = "142.250.186.14"; // 谷歌官方IP(需动态更新) if (!expectedIp.equals(address.getHostAddress())) { // DNS解析异常,触发警告 showToast("DNS可能被劫持!"); } } catch (UnknownHostException e) { e.printStackTrace(); }
- 多 DNS 服务器校验:
同时请求 Google DNS(8.8.8.8)和 Cloudflare DNS(1.1.1.1),对比解析结果是否一致4。
2. 系统 API 获取 DNS 配置
- Android 10 + 获取当前 DNS 服务器:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { NetworkCapabilities capabilities = connectivityManager.getNetworkCapabilities(network); List<InetAddress> dnsServers = capabilities.getDnsServers(); // 检查是否包含恶意DNS(如192.168.1.253) }
- 面试追问:为什么 Android 7.0 后禁止直接修改全局 DNS?
答:出于安全考虑,防止恶意 APP 篡改网络设置,需通过Settings
或设备管理员权限配置21。
四、Android 开发中的防护方案(高频考点:代码与系统配置)
1. 强制使用 HTTPS 并校验证书
- 网络安全策略配置:
在AndroidManifest.xml
中禁用明文流量,并固定证书:<network-security-config> <base-config cleartextTrafficPermitted="false"> <trust-anchors> <certificates src="system" /> <!-- 仅信任系统证书 --> <certificates src="@raw/bank_ca" /> <!-- 证书固定 --> </trust-anchors> </base-config> </network-security-config>
- 面试考点:证书固定(Certificate Pinning)的作用?
答:即使 DNS 被劫持,若服务器证书与本地固定证书不匹配,请求会失败,防止连接到伪造站点18。
2. 自定义 DNS 解析器(绕过系统 DNS)
- 使用 OkHttp 自定义 DNS:
OkHttpClient client = new OkHttpClient.Builder() .dns(new Dns() { @Override public List<InetAddress> lookup(String hostname) throws UnknownHostException { // 自定义DNS解析逻辑(如使用阿里云公共DNS) return InetAddress.getAllByName(hostname); } }) .build();
- 面试问题:为什么使用 HTTPDNS 能防劫持?
答:HTTPDNS 通过 HTTP 协议直接向权威服务器查询 IP,绕过运营商 DNS,避免劫持1128。
3. 启用加密 DNS 协议(DoT/DoH)
- Android 9 + 配置 DoT:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { DnsOverTls dnsOverTls = DnsOverTls.create(network, "dns.cloudflare.com"); // 应用层使用该DNS解析器 }
- DoT 与 DoH 的区别:
- DoT:基于 TLS 协议,端口 853,传输层加密,Android 9 + 原生支持9。
- DoH:基于 HTTPS 协议,端口 443,应用层加密,Android 10 + 浏览器支持27。
4. 检测本地 hosts 文件篡改
- 读取 hosts 文件并校验:
try { BufferedReader reader = new BufferedReader(new FileReader("/system/etc/hosts")); String line; while ((line = reader.readLine()) != null) { if (line.contains("恶意域名") || line.contains("可疑IP")) { // 触发安全警告 } } } catch (IOException e) { e.printStackTrace(); }
五、设备与系统层面的防护(面试需结合 Android 特性)
-
路由器与网络环境加固:
- 修改路由器默认密码,关闭远程管理权限,启用 DNS 防劫持功能(如小米路由器的 “DNS 过滤”)7。
- 优先使用家庭 Wi-Fi,避免公共网络;必要时使用 VPN 加密流量14。
-
系统级防护策略:
- Android 9 + 私有 DNS:
在设置中配置私有 DNS(如dns.google
),强制所有网络请求使用加密解析10。 - 权限控制:
拒绝应用获取CHANGE_NETWORK_STATE
等危险权限,防止篡改 DNS 设置12。
- Android 9 + 私有 DNS:
-
用户引导与教育:
- 提示用户手动设置公共 DNS(如 8.8.8.8),并定期检查网络配置24。
- 关键场景(如网银)使用 HTTPS 并验证证书有效性6。
六、面试高频追问与参考答案
-
“为什么 HTTPS 不能完全防止 DNS 挟持?”
- 答:HTTPS 仅加密数据传输,若域名解析被劫持,用户会先连接到恶意服务器,后续加密无意义。需结合证书固定和加密 DNS(DoT/DoH)212。
-
“Android 中如何实现 DNS 劫持的实时监控?”
- 答:通过
ConnectivityManager
监听网络变化,每次网络切换后触发域名解析校验(如解析baidu.com
并对比官方 IP),若连续多次解析错误则触发警报315。
- 答:通过
-
“如果用户连接的 Wi-Fi 被劫持,APP 如何保证安全?”
- 答:① 强制使用 HTTPS 并启用证书固定;② 在 APP 内集成 DoH/DoT 解析器,绕过系统 DNS;③ 检测到异常解析时提示用户切换网络或使用 VPN69。
-
“为什么有些 APP 在被 DNS 挟持后仍能正常访问?”
- 答:若 APP 直接使用 IP 地址而非域名(如硬编码服务器 IP),或使用了内置的 DNS 解析库(如 OkHttp 自定义 DNS),可绕过系统 DNS 设置,但存在维护成本高、IP 变更无法自动更新的问题1117。
总结(面试加分项)
DNS 挟持的核心是篡改域名解析结果,而 Android 开发者需从 ** 应用层加密(HTTPS / 证书固定)、系统层配置(DoT/DoH/ 私有 DNS)、用户层引导(手动设置公共 DNS)** 三方面防范。