iOS 11 中网络层的一些变化

作者:作者:casa ,博客:https://casatwy.com

本文将介绍 iOS11 下网络层(NSURLSession)的一些变化。这篇文章分4部分来总结了Session 707和709:

  1. iOS11中网络层新出的功能
  2. iOS11中网络层优化的功能
  3. iOS11中网络层的最佳实践
  4. 苹果对网络层未来的规划

因为这一次很多内容其实是苹果用新的技术来给网络层做的优化,所以新功能优化很难界定。我这边采用的判断标准是:

  1. 如果是需要工程师代码配合的优化,就算是新功能
  2. 如果是不需要工程师代码配合的,哪怕是应用了新技术,我也把它归类为优化

1. iOS11中网络层新出的功能

1.1 Network Extension Framework有新API

提供了两个新类:NEHotSpotConfiguration、NEDNSProxyProvider。

  • NEHotSpotConfiguration

NEHotSpotConfiguration可以让你的智能设备在链接手机App之后,能够很方便地通过在手机App上的操作来实现热点的链接。

例如你买了一个网络摄像头,你想要连上摄像头的Wi-Fi热点去配置这个摄像头的话,以前要这么操作:

现在用NEHotSpotConfiguration就能很方便地搞定事情了:

当然,这套API也可以被拿来模拟各种网络环境,在测试App的时候很有用。

  • NEDNSProxyProvider

NEDNSProxyProvider可以用来设置你的手机如何跟DNS做交互。你可以自己发DNS请求,也可以自己基于不同的协议去做DNS查询。例如DNS over TLS,DNS over HTTP。

1.2 可以进行多路多协议的网络操作(Multipath Protocols for Mobile Devices)

在之前的iOS版本中,网络如果要从Wi-Fi模式变成Cellular(2/3/4G)模式,那就是先断掉Wi-Fi然后再连上Cellular,然后数据包就从原来的Wi-Fi链路迁移到Cellular链路去发送。

在链路切换过程中链接肯定就断掉了,为了解决这个问题,苹果爸爸搞了Multipath Protocols for Mobile Devices(移动设备多路协议),这使得移动设备的TCP包可以在这两个(多个)链路上随意切换着发(同时开启两个流量链路),而不必断线重连。

效果就是:Wi-Fi和Cellular可以共存,相互辅助。有三个模式,前两个可选,第三个只能自己私底下玩:

  • Handover Mode(高可靠模式)

这种模式下优先考虑的是链接的可靠性。只有在Wi-Fi信号不好的时候,流量才会走Cellular。如果Wi-Fi信号好,但是Wi-Fi很慢,这时候也不会切到Cellular链路。

这种模式在beta版中已经支持了。

  • Interactive Mode(低延时模式)

这种模式下优先考虑的是链接的低延时。系统会看Wi-Fi快还是Cellular快。如果Cellular比Wi-Fi快,哪怕此时Wi-Fi信号很好,系统也会把流量切到Cellular链路。

这种模式在未来的beta版会支持。

  • Aggregation Mode(混合模式)

在这种模式下,Wi-Fi和Cellular会同时起作用。如果Wi-Fi是1G带宽,Cellular也是1G带宽,那么你的设备就能享受2G带宽。

嗯,很好很强大。但你只能拿来玩,不能生产环境中使用。

因为苹果爸爸不想让你用。其实他说的理由是希望开发者自己好好考虑1和2用哪个,因为如果3也能用的话,苹果爸爸知道你们就完全不会考虑1和2了,直接用3了。反正用户流量不是开发者掏钱。

这个模式只能够在系统设置里面自己开启来玩,不能像1和2那样可以让开发者在应用中通过NSURLSessionConfiguration.multipathServiceType自己选。

需要注意的是,Multipath Protocols for Mobile Devices这个功能同时也需要服务端支持MPTCP(Multipath TCP)才行,如果服务端不支持的话,光客户端支持没用。linux起了一个项目在做这个事情,项目地址:https://multipath-tcp.org。有兴趣的同学可以自己去看一下。

1.3 ProgressReporting协议

iOS11提供了ProgressReporting协议,并且NSURLSessionTask实现了这个协议,让你能够获得progress对象,这个progress对象可以以0~1.0的方式告诉你当前进度,而不用你自己去拿到已获得的数据量去除以需要获得的数据总量从而得出进度。因为有的时候你并不一定能够拿到数据总量。然后这个progress对象跟NSURLSessionTask的绑定是双向的:你调用progress对象的cancel、pause、resume也会使得task变为cancel、pause、resume,反之亦然。

1.4 URLSessionStreamTask 和 authentication proxies

你需要使用URLSessionStreamTask去替代之前的NSInputStream/NSOutputStream,它支持:

  1. 使用host和port来进行TCP/IP连接
  2. 支持基于STARTTLS的安全握手协议
  3. 支持Navigation of authenticating HTTPS Proxies。事实上就是:如果你是通过proxy访问的网络,当proxy问你要证书的时候,iOS11会自动帮你从keychain里面找到证书给出去。

1.5 URLSession Adaptable Connectivity API

以前进行网络调用时,如果网络不通,那么系统就会报个错告诉你网络不通。这时候你要么轮询要么让用户手动retry,然后网络通了请求才能发送出去。

现在你可以这么做:如果请求发送的时候网络不通,那么这个请求就会等到网络通了的时候再发出去。于是你就不用轮询了,用户也不用手动retry了。

具体做法:把NSURLSessionConfiguration的waitsForConnectivity设置成YES,拿它去生成NSURLSession去使用就好了。

1.6 URLSessionTask Scheduling API

这其实算是一种优化,但还是需要工程师代码配合的,它主要解决了三个问题:

  1. 没必要因为创建NSURLSessionTask而进行额外的后台加载
  2. 当后台请求创建但还没发出去的时候,这个被创建的请求有可能因为上下文变化的原因导致这个请求无意义
  3. iOS系统并不知道什么时候去发起你的请求才是最合适的

原先你要做后台数据加载的时候,流程是这样的:

现在iOS11里,苹果把头两个步骤合并为一个步骤了:

所以当应用在前台的时候,你就可以创建这个NSURLSessionTask,iOS11里面就不会在后台额外launch一次去创建NSURLSessionTask。这就解决了问题1。

iOS11在NSURLSessionTask的delegate里面提供了一个新的方法:urlSession:task:willBeginDelayedRequest:completionHandler:。系统在发起请求之前会调一个这个回调,然后在这个completionHandler里面你告诉系统这个请求是否要发出去,是否要修改。从而解决了问题2。

iOS11也给了NSURLSessionTask一个property:earliestBeginDate。系统在earlistBeginDate之前是不会发起这个请求的。你给这个task设置一个earliestBeginDate,就解决了问题3。

最后小胖子又补充了一下,你可以通过设置NSURLSessionTask的countOfBytesClientExpectsToSend和countOfBytesClientExpectsToReceive来让系统更好地调度你的后台网络任务。

2. iOS11中网络层优化的功能

2.1 Explicit Congestion Notification(ECN,显式拥塞通知)

top Created with Sketch.