11.04 网络编程-常用网络知识

2025/4/22 Android 开发基础

前三节覆盖了 HTTP 主线(基础 → OkHttp → Retrofit → 进阶封装)。本节补充日常开发中"经常遇到但主线不讲"的网络知识:协议演进(HTTP/2、HTTP/3 与 QUIC)、HTTPDNS、网络状态监测、抓包调试工具链、断点续传下载、MQTT、流量统计。它们各自不成体系,但都是工程里绕不开的"常识点"。

# 一、HTTP 协议演进:1.1 → 2 → 3

HTTP协议演进示意图

主线脉络:每一代都在解决"队头阻塞":

  • 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,存在两大问题:

  1. 域名劫持/污染:LocalDNS 返回广告页 IP 或错误 IP
  2. 解析不准: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 的安全加固):

  1. 抓包工具证书要被设备信任:Android 7.0+ 应用默认只信任系统证书,调试需在 manifest 加 android:networkSecurityConfig,debug 配置信任用户证书(release 不带)
  2. 若应用做了证书锁定:抓包工具的证书不在 pin 列表里,直接握手失败——这恰恰说明锁定生效;调试时用 debug 变体关闭 pin
  3. 抓包只能抓到明文流量;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 项目。

# 七、常见问题与陷阱

  1. NetworkCallback 收不到回调:register 后记得 unregister(页面/Service 生命周期配对),重复注册会回调多次。
  2. HTTPDNS + HTTPS 组合报证书错:直连 IP 但 Host 头没带原域名,或 SNI 处理不对——证书校验基于域名,Host 必须保留。
  3. 断点续传出现文件损坏:服务端内容在两次下载间变化了——续传前比对 ETag,不一致就重新下载。
  4. DownloadManager 拿不到回调:它通过 ContentObserver 查询进度(不同于普通回调模型),或配合 WorkManager 自己实现可控下载。
  5. Wi-Fi 与蜂窝同时在线走哪个:系统默认路由决定,NetworkRequest 的 transport 能力可以指定业务走哪个网络(如大文件只走非计费网络)。
  6. 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)。