在 PC 互联网时代,网络优化已经是一项非常复杂的工作。对于移动网络来说,弱网络、网络切换、网络劫持这些问题更加突出,网络优化这项工作也变得更加艰巨。
本文主要记载
- 1 网络库实现哪家强?
- 2 网络需要优化哪些?
- 2.1 HTTPDNS
- 2.2 链接复用
- 2.3 压缩与加密
- 2.4 其他优化
- 3 如何监控网络
- 4 PLT HOOK Demo 练习
1 网络库实现哪家强?
在实际的开发工作中,我们很少会像《UNIX 网络编程》那样直接去操作底层的网络接口,一般都会使用网络库。Square 出品的 OkHttp 是目前最流行的 Android 网络库,它还被 Google 加入到 Android 系统内部,为广大开发者提供网络服务。
网络库的作用是屏蔽了下层复杂的网络接口,让我们可以更高效地使用网络请求。主要有以下三点:
- 统一编程接口:无论是同步还是异步请求,接口都非常简单易用。同时我们可以统一做策略管理,统一进行流解析(
JSON、XML、Protocol Buffers)等。 - 全局网络控制:在网络库内部我们可以做统一的网络调度、流量监控以及容灾管理等工作。
- 高性能:既然我们把所有的网络请求都交给了网络库,那网络库是否实现高性能就至关重要。既然要实现高性能,那我会非常关注速度,
CPU、内存、I/O的使用,以及失败率、崩溃率、协议的兼容性等方面。
接下来对比 OkHttp、Chromium 的 Cronet 以及微信 Mars 这三个网络库的内部实现。
蘑菇街 、头条、UC 浏览器、微信 Mars 都在 Chromium 网络库上做了二次开发,而微信 Mars 在弱网络方面做了大量优化,拼多多、虎牙、链家、美丽说这些应用都在使用 Mars。
为什么那么多大厂都在用Chromium 的 Cronet 做二次开发,而不用 OkHTTP 呢?主要因为它并不支持跨平台,对于大型应用来说跨平台是非常重要的。我们不希望所有的优化 Android 和 iOS 都要各自去实现一套,不仅浪费人力而且还容易出问题。对于 Mars 来说,它是一个跨平台的 Socket 层解决方案,并不支持完整的 HTTP 协议,所以 Mars 从严格意义上来讲并不是一个完整的网络库。但是它在弱网络和连接上做了大量的优化,并且支持长连接。
Chromium 网络库作为标准的网络库,基本上可以说是找不到太大的缺点。而且我们可以享受 Google 后续网络优化的成果,类似 TLS 1.3、QUIC 支持等。但是它针对弱网络场景没有做太多定制的优化,也不支持长连接。
2 网络需要优化哪些?
在讲怎么去优化网络之前,先明确一下所谓的网络优化,究竟指的是什么?
- 速度:在网络正常或者良好的时候,怎样更好地利用带宽,进一步提升网络请求速度。
- 弱网络:移动端网络复杂多变,在出现网络连接不稳定的时候,怎样最大程度保证网络的连通性。
- 安全:网络安全不容忽视,怎样有效防止被第三方劫持、窃听甚至篡改。
网络请求的整个过程

- DNS 解析:通过
DNS服务器,拿到对应域名的IP地址。在这个步骤,我们比较关注DNS解析耗时情况、运营商LocalDNS的劫持、DNS调度这些问题。 - 创建连接:跟服务器建立连接,这里包括
TCP三次握手、TLS密钥协商等工作。多个IP/端口该如何选择、是否要使用HTTPS、能否可以减少甚至省下创建连接的时间,这些问题都是我们优化的关键。 - 发送 / 接收数据:在成功建立连接之后,就可以愉快地跟服务器交互,进行组装数据、发送数据、接收数据、解析数据。我们关注的是,如何根据网络状况将带宽利用好,怎么样快速地侦测到网络延时,在弱网络下如何调整包大小等问题。
- 关闭连接:连接的关闭看起来非常简单,其实这里的水也很深。这里主要关注主动关闭和被动关闭两种情况,一般我们都希望客户端可以主动关闭连接。
🔥🔥 所谓的网络优化,就是围绕速度、弱网络、安全这三个核心内容,减少每一个步骤的耗时,打造快速、稳定且安全的高质量网络。
2.1 HTTPDNS
DNS 的解析是网络请求的第一项工作,默认使用运营商的 LocalDNS 服务。这块耗时在 3G 网络下可能是 200~300ms,4G 网络也需要 100ms。
解析慢并不是默认 LocalDNS 最大的 “原罪” ,它还存在一些其他问题:
- 稳定性:
UDP协议,无状态,容易域名劫持(难复现、难定位、难解决),每天至少几百万个域名被劫持,一年至少十次大规模事件。 - 准确性:
LocalDNS调度经常出现不准确,比如北京的用户调度到广东IP,移动的运营商调度到电信的IP,跨运营商调度会导致访问慢,甚至访问不了。 - 及时性:运营商可能会修改
DNS的TTL,导致DNS修改生效延迟。不同运营商的服务实现不一致,我们也很难保证DNS解析的耗时。
为了解决这些问题,就有了 HTTPDNS。简单来说自己做域名解析的工作,通过 HTTP 请求后台去拿到域名对应的 IP 地址,直接解决上述所有问题。
新浪 HTTPDNS库 | Android端HttpDNS优化方案 | Android OkHttp实现HttpDns的最佳实践(非拦截器)
2.2 连接复用
利用 HTTP 协议里的 keep-alive,而 HTTP/2.0 的多路复用则可以进一步的提升连接复用率。它复用的这条连接支持同时处理多条请求,所有请求都可以并发在这条连接上进行。
虽然 H2 十分强大,不过这里还有两个问题需要解决。
- 一个是同一条
H2连接只支持同一个域名 - 一个是后端支持
HTTP/2.0需要额外的改造
这个时候我们只需要在统一接入层(传说中的中台?)做改造,接入层将数据转换到 HTTP/1.1 再转发到对应域名的服务器。

这样所有的服务都不用做任何改造就可以享受 HTTP/2.0 的所有优化,不过这里需要注意的是 H2 的多路复用在本质上依然是同一条 TCP连接,如果所有的域名的请求都集中在某一条连接中,在网络拥塞的时候容易出现 TCP 队首阻塞问题。
2.3 压缩与加密
2.3.1 压缩
首先对于 HTTP 请求来说,数据主要包括三个部分:请求 URL、请求 header、请求 body。
对于请求 body 来说,一方面是数据通信协议的选择,在网络传输中目前最流行的两种数据序列化方式是 JSON 和 Protocol Buffers。Protocol Buffers 使用起来更加复杂一些,但在数据压缩率、序列化与反序列化速度上面都有很大的优势。
另外一方面是压缩算法的选择,通用的压缩算法主要是如 gzip,Google 的 Brotli 或者 Facebook 的 Z-standard 等算法,用来减少流量消耗和传输时间。
针对图片我们可以使用 webp、hevc、SharpP 等压缩率更高的格式。
还有就是 gzip 拦截器要添加到签名等操作拦截器之后,我们网络库在上传前需要使用 body 生成签名,如果在生成签名之前先使用 gzip 压缩了 body 体会什么也拿不到。
2.3.2 安全
数据安全也是网络重中之重的一个环节,在大网络平台中我们都是基于 HTTPS 的 HTTP/2 通道,已经有了 TLS 加密。
但是 HTTPS 带来的代价也是不小的,它需要 2-RTT 的协商成本,在弱网络下时延不可接受。同时后台服务解密的成本也十分高昂,在大型企业中需要单独的集群来做这个事情。
HTTPS 的优化有下面几个思路:
- 连接复用率。通过多个域名共用同一个 HTTP/2 连接、长连接等方式提升连接复用率。
- 减少握手次数。 TLS 1.3 可以实现
0-RTT协商,事实上在TLS 1.3 release之前,微信的 mmtls、Facebook的fizz、阿里的SlightSSL都已在企业内部大规模部署。 - 性能提升。使用
ecc证书代替RSA,服务端签名的性能可以提升4~10倍,但是客户端校验性能降低了约20倍,从10微秒级降低到100微秒级。另外一方面可以通过Session Ticket会话复用,节省一个RTT耗时。
使用 HTTPS 之后,整个通道是不是就一定高枕无忧呢?如果客户端设置了代理,TLS 加密的数据可以被解开并可能被利用 。这个时候我们可以在客户端将 证书锁定(Certificate Pinning),为了老版本兼容和证书替换的灵活性,建议锁定根证书。
我们也可以对传输内容做二次加密,这块在统一接入层实现,业务服务器也同样无需关心这个流程。需要注意的是二次加密会增加客户端与服务器的处理耗时,我们需要在安全性与性能之间做一个取舍。

2.4 其他优化
这个两个在直播场景经常使用
关于网络优化的手段还有很多,一些方案可能是需要用钱堆出来的,比如部署跨国的专线、加速点,多 IDC 就近接入等。
QUIC
QUIC协议由 Google 在 2013 年实现,在2018年基于QUIC协议的HTTP更被确认为 HTTP/3 。在连接复用中说过HTTP/2 + TCP会存在队首阻塞的问题,基于UDP的QUIC才是终极解决方案。QUIC简单理解为HTTP/2.0 + TLS 1.3 + UDP
3 如何监控网络?
3.1 NetWork Profiler
涨姿势:利用AndroidStudio自带的Network Profiler来查看网络请求的相关数据
3.2 Charles
3.3 插桩
360 开源的性能监控工具 ArgusAPM 就是利用 Aspect 切换插桩,实现监控系统和 OkHttp 网络库的请求。
系统网络库的插桩实现可以参考 TraceNetTrafficMonitor,主要利用 Aspect 的切面功能。OkHttp 的拦截可以参考OkHttp3Aspect,它会更加简单一些,因为 OkHttp 本身就有代理机制。
1 |
|
3.4 Native Hook
网络相关的一般会 Hook 下面几个方法 :
- 连接相关:
connect。 - 发送数据相关:
send和sendto。 - 接收数据相关:
recv和recvfrom。
具体 Demo 见 4 PLT HOOK Demo 练习
4 PLT HOOK Demo 练习
它的作用是不仅仅 hook 某个 so ,而是 hook 内存中的所有 so 。但是要排除掉方法本身定义的 so ,不然运行期间会出问题。
Demo 中比较疑惑的点 ,hook_plt_method_all_lib 的第一个参数是要排除掉的 so 库。还在研究中。。。
通过 connect 函数的 hook ,我们可以做的东西很多,例如:
- 禁用应用网络访问
- 过滤广告
ip - 禁用定位功能
感谢
以及上文中的链接
更多理论和基础知识







