11.04 网络编程-常用网络知识
前三节覆盖了 HTTP 主线(基础 → OkHttp → Retrofit → 进阶封装)。本节补充日常开发中"经常遇到但主线不讲"的网络知识:协议演进(HTTP/2、HTTP/3 与 QUIC)、HTTPDNS、网络状态监测、抓包调试工具链、断点续传下载、MQTT、流量统计。它们各自不成体系,但都是工程里绕不开的"常识点"。
# 一、HTTP 协议演进:1.1 → 2 → 3
主线脉络:每一代都在解决"队头阻塞":
- HTTP/1.1:响应按序返回,一个慢请求堵住整条连接(应用层缓解:多域名并行连接)
- HTTP/2:二进制分帧 + 多路复用,一条 TCP 连接并发多流;但 TCP 本身的丢包重传仍会阻塞所有流(传输层队头阻塞)
- HTTP/3(QUIC):基于 UDP 重建传输层,流之间完全独立;附带 0-RTT 建连、内置 TLS1.3、连接迁移(WiFi 切蜂窝连接不断)
客户端实践要点:OkHttp 4.x 对 HTTP/2 自动协商(ALPN),HTTP/3 在 OkHttp 5+ 支持;业务层无需感知协议版本,但要知道"升级协议 = 服务端/网关配合",App 端只是受益方。另外,理解了协议演进,"一个域名散列到多个 CDN 子域名"这类老优化在 HTTP/2 下反而是反模式(多连接破坏多路复用)。
# 二、HTTPDNS:绕过 LocalDNS 的劫持与污染
11.00 生命周期第一步是 DNS 解析,而传统解析走运营商 LocalDNS,存在两大问题:
- 域名劫持/污染:LocalDNS 返回广告页 IP 或错误 IP
- 解析不准:CDN 调度失准(LocalDNS 的出口 IP 不代表用户真实位置),跨网访问慢
HTTPDNS 的思路:App 内直接向 HTTPDNS 服务(阿里云/腾讯云等)通过 HTTP 请求拿到域名 IP,绕过 LocalDNS,然后带着 Host 头直连该 IP:
// 思路示意(配合11.01拦截器):
// 1. 请求前先从HTTPDNS服务拿到IP
// 2. URL保持域名不变(TLS SNI和证书校验依赖域名),连接时替换目标IP
public class HttpDnsInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
String host = request.url().host();
String ip = HttpDnsService.getIpByHost(host); // 自建或云服务
if (ip != null && !ip.equals(host)) {
// 用IP直连,Header里保留原Host(服务端虚拟主机与证书校验依赖它)
Request newRequest = request.newBuilder()
.url(request.url().newBuilder().host(ip).build())
.header("Host", host)
.build();
return chain.proceed(newRequest);
}
return chain.proceed(request);
}
}
适用判断:业务量大、对解析劫持敏感(金融/音视频/电商大促)才值得引入;小应用用系统 DNS 足够。
# 三、网络状态监测:ConnectivityManager 与 NetworkCallback
弱网与断网处理的前提是"知道网络状态"。现代 API 用 NetworkCallback(替代过时的 CONNECTIVITY_ACTION 广播):
public class NetworkMonitor {
private ConnectivityManager mCM;
public void register(Context context, NetworkCallback callback) {
mCM = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkRequest request = new NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.build();
// 注册回调:网络可用/丢失/切换(Wi-Fi↔蜂窝)都会通知
mCM.registerNetworkCallback(request, callback);
}
public static abstract class Callback extends ConnectivityManager.NetworkCallback {
@Override
public void onAvailable(Network network) {
// 网络恢复:触发重连(WebSocket重连、暂停的下载续跑)
}
@Override
public void onLost(Network network) {
// 网络丢失:暂停请求、提示用户
}
@Override
public void onCapabilitiesChanged(Network network, NetworkCapabilities caps) {
// 能力变化:带宽/是否计费(metered)——大流量任务可避开计费网络
boolean isWifi = caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI);
boolean isMetered = !caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
}
}
// 主动查询:当前是否联网
public static boolean isOnline(Context context) {
ConnectivityManager cm = (ConnectivityManager)
context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkCapabilities caps = cm.getNetworkCapabilities(cm.getActiveNetwork());
return caps != null && caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
}
}
工程要点:网络切换时长连接必须重连(IP 变了,旧 TCP 已死);"判断了 isOnline 仍可能请求失败"(连接存在但不通),状态监测只是优化手段,错误兜底仍是必需的。需要精细化弱网判断时可采样请求 RTT/成功率做网络质量打分(大厂网络库标配)。
# 四、抓包调试工具链
日常排查接口问题,抓包是最直接的手段:
| 工具 | 平台 | 用途 |
|---|---|---|
| Charles | 桌面 | HTTPS 抓包(安装并信任 Charles 证书)、断点修改响应、弱网模拟(Throttle) |
| mitmproxy | 桌面/命令行 | 脚本化抓包与改写(自动化测试) |
| Android Studio Network Inspector | AS 内置 | App 内网络面板:请求列表、耗时、payload,无需代理 |
| OkHttp logging-interceptor | App 内 | logcat 里直接看请求/响应(11.01),最快最轻 |
HTTPS 抓包的条件(对照 11.03 的安全加固):
- 抓包工具证书要被设备信任:Android 7.0+ 应用默认只信任系统证书,调试需在 manifest 加
android:networkSecurityConfig,debug 配置信任用户证书(release 不带) - 若应用做了证书锁定:抓包工具的证书不在 pin 列表里,直接握手失败——这恰恰说明锁定生效;调试时用 debug 变体关闭 pin
- 抓包只能抓到明文流量;QUIC/HTTP3 流量需要工具支持(新版 Charles 支持)
弱网模拟:Charles Throttle 或模拟器
telnet console网络 speed 设置,验证加载态/重试逻辑/超时兜底是否齐全。
# 五、断点续传下载
大文件下载(安装包、离线包)的两个标配:Range 断点续传 与 DownloadManager/自建下载管理:
// 原理:HTTP Range头指定从哪个字节开始读,服务端返回206 Partial Content
public class ResumableDownloader {
public void download(File target, String url) throws IOException {
long downloaded = target.exists() ? target.length() : 0; // 已下载的字节数
Request request = new Request.Builder()
.url(url)
.header("Range", "bytes=" + downloaded + "-") // 从断点继续
.build();
Response response = OkHttpHolder.getClient().newCall(request).execute();
if (response.code() == 206 && response.body() != null) {
// "a"模式追加写入,接上之前的进度
try (OutputStream out = new FileOutputStream(target, true);
InputStream in = response.body().byteStream()) {
byte[] buf = new byte[8192];
int len;
while ((len = in.read(buf)) != -1) {
out.write(buf, 0, len);
if (cancelled) { call.cancel(); break; }
}
}
} else {
// 服务端不支持Range(返回200全量):删掉旧文件重下
}
}
}
- 校验完整性:下载完成后比对
ETag/Content-MD5/文件哈希 - 系统级方案:
DownloadManager(免维护后台下载、自动重试,但定制性差);应用内多线程分块下载(Range 分段)是下载器 SDK 的核心原理 - 流量统计:
TrafficStats可按 UID 统计收发字节数(做流量提醒/限额)
# 六、MQTT:IoT/推送场景的轻量协议
除了 HTTP 和 WebSocket,MQTT 是IoT 设备、智能硬件、推送通道常用协议——发布/订阅模型:
┌────────┐ publish(topic/sensor/data) ┌─────────┐ subscribe(topic) ┌────────┐
│ 设备端 │ ────────────────────────────→ │ Broker │ ──────────────────→ │ 手机App │
└────────┘ └─────────┘ └────────┘
QoS 0(至多一次) / QoS 1(至少一次) / QoS 2(恰好一次)
Retained消息 / 遗嘱消息(LWT) / Topic通配符
与 WebSocket 的关系:MQTT 也可跑在 WebSocket 之上;移动端库常用 Paho MQTT。选型直觉:通用推送/IM → WebSocket 或厂商通道;设备遥测/智能家居 → MQTT。只需建立概念认知,深入使用多在 IoT 项目。
# 七、常见问题与陷阱
- NetworkCallback 收不到回调:register 后记得 unregister(页面/Service 生命周期配对),重复注册会回调多次。
- HTTPDNS + HTTPS 组合报证书错:直连 IP 但 Host 头没带原域名,或 SNI 处理不对——证书校验基于域名,Host 必须保留。
- 断点续传出现文件损坏:服务端内容在两次下载间变化了——续传前比对
ETag,不一致就重新下载。 - DownloadManager 拿不到回调:它通过
ContentObserver查询进度(不同于普通回调模型),或配合WorkManager自己实现可控下载。 - Wi-Fi 与蜂窝同时在线走哪个:系统默认路由决定,
NetworkRequest的 transport 能力可以指定业务走哪个网络(如大文件只走非计费网络)。 - HTTP/3 无感但抓不到包:老抓包工具不支持 QUIC,可临时禁用 QUIC 强制回落 HTTP/2 抓包。
# 结束
本节补全了主线之外的高频网络知识:HTTP 协议演进(1.1/2/3 的队头阻塞主线,配演进图)、HTTPDNS(绕过 LocalDNS 劫持,拦截器实现思路)、网络状态监测(NetworkCallback 与切换处理)、抓包调试工具链(Charles/mitmproxy/Network Inspector 与 HTTPS 抓包条件)、断点续传下载(Range/206 原理与实现)、MQTT(IoT 场景的发布订阅模型)与流量统计。至此第 11 章网络编程全部完成:基础 → OkHttp → Retrofit → 进阶封装 → 常识补全。下一节:12 数据解析。更多内容请参考:网络连接概览 (opens new window)。