网络通讯网络请求More Stronger

深入了解NSURLSession

2017-03-24  本文已影响1327人  Jerry4me

Github : Jerry4me, Demo : JRBgSessionDemo

<a>前言</a>


本文主要是结合官方文档, 挖掘NSURLSession的类层次结构及其联系, 总结出关于NSURLSession的一些关键点及其用法.

关于NSURLSession为什么能取代NSURLConnection, 其优势是什么, 及其NSURLSession API的概述, 见
关于ATS, HTTP/2, 以及iOS9 NSURLSession新特性 : sharedCookies, streamTask和taskMetrics, 见

以上两篇文章都是我看wwdc视频然后总结出来的文章, 大家感兴趣的可以先了解了解. 如果不想知道那么多, 只想知道怎么用NSURLSession, 那就直接看本文的正文.

*了解URL Loading System

<a>目录</a>



<a id = "NSURLSession">NSURLSession</a>


NSURLSession

ALPN与NPN

NPN是服务端发送它支持的HTTP协议列表, 供客户端选择; 而ALPN则相反, 由客户端发送它支持的HTTP协议列表, 供服务端选择. 如果缺少NPN/ALPN其中一个, 则无法使用HTTP/2通信. 具体请见为什么我们应该尽快支持 ALPN.

NSURLSession相关类为 :

他们相互的关系如下 :

NSURLSession

session分为 :

session为哪一种类型完全由其内部的Configuration而定.

NSURLSessionConfiguration

配置分为 :

另外, 我们还可以给Configuration对象再自定义一些属性, 例如每端口的最大并发HTTP请求数目, 以及是否允许蜂窝网络, 请求缓存策略, 请求超时, cookies/证书存储策略等等

NSURLSessionDelegate

session管理的一组tasks共享一个代理, 不想实现代理方法时, 代理传nil即可.
代理协议分为 :

NSURLSessionTask

session task类型分为 :

NSURLSessionTaskMetrics 和 NSURLSessionTaskTransactionMetrics

对发送请求/DNS查询/TLS握手/请求响应等各种环节时间上的统计. 更易于我们检测, 分析我们App的请求缓慢到底是发生在哪个环节, 并对此进行优化提升我们APP的性能.

NSURLSessionTaskMetrics对象与NSURLSessionTask对象一一对应. 每个NSURLSessionTaskMetrics对象内有3个属性 :

API很简单, 就一个方法 : - (void)URLSession: task: didFinishCollectingMetrics:, 当收集完成的时候就会调用该方法.

<a id = "身份验证和自定义TLS">身份验证和自定义TLS</a>


  1. 当一个服务器请求身份验证或TLS握手期间需要提供证书的话, URLSession会调用他的代理方法URLSession:​did​Receive​Challenge:​completion​Handler:​去处理.

  2. 如果你没有实现该代理方法, URLSession就会这么做 :

  1. 如果证书还是不可用或服务器拒绝该证书, 就会继续缺少身份认证的连接.

<a id = "App Transport Security">App Transport Security</a>


从iOS9开始支持ATS, 且默认ATS只支持发送HTTPS请求, 不允许发送不安全的HTTP请求. 如果用户需要发送HTTP请求需要在info.plist中配置一些东西.

详情在文章开头的iOS9 ATS HTTP/2 NSURLSession中说得很详细, 想了解的可以进去阅读.

<a id = "NSURLSession 工作流程">NSURLSession 工作流程</a>


那么如何使用NSURLSession像从前用NSURLConnection那样发送一个请求呢?

// 设置配置
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
/** 设置其他配置属性 **/

// 代理队列
NSOperationQueue *queue = [NSOperationQueue mainQueue];

// 创建session
NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:queue];

// 利用session创建n个task
NSURLSessionDownloadTask *task = [session downloadTaskWithURL:[NSURL URLWithString:@"https://www.baidu.com"]];
// 开始
[task resume];

然后就可以在代理方法中处理各种事情了. 简单吧? 下面分task说明代理方法的调用情况..

身份验证或TLS握手

这是所有task都必须经历的一个过程. 当一个服务器请求身份验证或TLS握手期间需要提供证书的话, URLSession会调用他的代理方法URLSession:​did​Receive​Challenge:​completion​Handler:​去处理., 另外, 如果连接途中收到服务器返回需要身份认证的response, 也会调用该代理方法.

重定位response

这也是所有task都有可能经历的一个过程, 如果response是HTTP重定位, session会调用代理的URLSession:​task:​will​Perform​HTTPRedirection:​new​Request:​completion​Handler:方法. 这里需要调用completionHandler告诉session是否允许重定位, 或者重定位到另一个URL, 或者传nil表示重定位的响应body有效并返回. 如果代理没有实现该方法, 则允许重定位直到达到最大重定位次数.

DataTask

  1. 对于一个data task来说, session会调用代理的URLSession:​data​Task:​did​Receive​Response:​completion​Handler:​方法, 决定是否将一个data dask转换成download task, 然后调用completion回调继续接收data或下载data.
  1. 在服务器传输数据给客户端期间, 代理会周期性地收到URLSession:​data​Task:​did​Receive​Data:​回调

  2. session会调用URLSession:​data​Task:​will​Cache​Response:​completion​Handler:​询问你的app是否允许缓存. 如果代理不实现这个方法的话, 默认使用session绑定的Configuration的缓存策略.

DownloadTask

  1. 对于一个通过download​Task​With​Resume​Data:​创建的下载任务, session会调用代理的URLSession:​download​Task:​did​Resume​At​Offset:​expected​Total​Bytes:​方法.

  2. 在服务器传输数据给客户端期间, 调用URLSession:​download​Task:​did​Write​Data:​total​Bytes​Written:​total​Bytes​Expected​To​Write:给用户传数据

  1. 如果download task成功完成了, 调用URLSession:​download​Task:​did​Finish​Downloading​To​URL:把临时文件的URL路径给你. 此时你应该在该代理方法返回以前读取他的数据或者把文件持久化.

UploadTask

上传数据去服务器期间, 代理会周期性收到URLSession:​task:​did​Send​Body​Data:​total​Bytes​Sent:​total​Bytes​Expected​To​Send:回调并获得上传进度的报告.

StreamTask

如果任务的数据是由一个stream发出的, session就会调用代理的URLSession:​task:​need​New​Body​Stream:​方法去获取一个NSInputStream对象并提供一个新请求的body data.

task completion

任何task完成的时候, 都会调用URLSession:​task:​did​Complete​With​Error:​方法, error有可能为nil(请求成功), 不为nil(请求失败)

Note
NSURLSession不会收到服务器传来的错误, 代理只会收到客户端出现的错误, 例如无法解析主机名或无法连接上主机等等. 客户端错误定义在URL Loading System Error Codes. 服务端错误通过HTTP状态法进行传输, 详情请看NSHTTPURLResponse和NSURLResponse类.

销毁session

如果你不再需要一个session了, 一定要调用它的invalidateAndCancelfinishTasksAndInvalidate方法. (前者是取消所有未完成的任务然后使session失效, 后者是等待正在执行的任务完成之后再使session失效). 否则的话, 有可能造成内存泄漏. 另外, session失效后会调用URLSession:​did​Become​Invalid​With​Error:方法, 之后session释放对代理的强引用.

<a id = "Background Transport">Background Transport</a>


需要注意的是, 在后台session中, 一些代理方法将失效. 下面说一些使用后台session的注意点 :

后台session限制确实很多, 所以尽可能使用前台session做事情.

Note

后台session最好用来传输一些支持断点续传大文件. 或对这个过程进行一些针对性的优化

  • 最好把文件先压缩成zip/tar等压缩文件再上传/下载.
  • 把大文件按数据段分别发送, 发送完之后服务端再把数据拼接起来.
  • 上传的时候服务端应该返回一个标识符, 这样可以追踪传输的状态, 及时做出传输的调整
  • 增加一个web代理服务器中间层, 以促进上述的优化

Usage

那么如何使用这个后台传输呢?

2017-03-24 14:17:09.458415 JRBgSessionDemo[2766:1080861] 下载中 - 58%
2017-03-24 14:17:09.567957 JRBgSessionDemo[2766:1080861] 下载中 - 59%
2017-03-24 14:17:16.916830 JRBgSessionDemo[2766:1080828] -[AppDelegate application:handleEventsForBackgroundURLSession:completionHandler:]
2017-03-24 14:17:16.951185 JRBgSessionDemo[2766:1080977] -[DownloadViewController URLSession:downloadTask:didFinishDownloadingToURL:]
2017-03-24 14:17:16.953951 JRBgSessionDemo[2766:1080977] -[DownloadViewController URLSession:task:didCompleteWithError:]
2017-03-24 14:17:16.954574 JRBgSessionDemo[2766:1080977] -[DownloadViewController URLSessionDidFinishEventsForBackgroundURLSession:]

总结后台传输

  1. 尽量用真机进行调试, 模拟器会跳过某一两个方法
  2. 只能进行upload/download task, 不能进行data task
  3. 不能使用带completionHandler的方法创建task, 否则程序直接挂掉
  4. Applecation里的completionHandler必须存储起来, 等你处理完所有事情之后再调用告诉系统可以进行Snapshot和挂起app了
  5. 后台下载最好支持断点续传, 因为任务有可能会被系统主动取消(例如系统性能下降了, 资源不够用的情况下)

后台传输的Demo在文章头部的地方, 也可以点这里进去

<a id = "NSURLSession API">NSURLSession API</a>


API总结

所有创建task的方法, 只要带有completionHandler这个参数的, 均表示为请求过程中不会触发代理方法. 所有不带有completionHandler这个参数的, 均会走代理方法流程.

如果你实现了URLSession:​did​Receive​Challenge:​completion​Handler:​方法又没有在该方法调用completionHandler, 请求就会遭到阻塞

断点续传

<a id = "Something else Important">Something else Important</a>


NSCopying Behavior

session, task和configuration对象都支持copy操作 :

线程安全

URLSession 的API全部都是线程安全的. 你可以在任何线程上创建session和tasks, task会自动调度到合适的代理队列中运行.

Warning

后台传输的代理方法URLSession​Did​Finish​Events​For​Background​URLSession:​可能会在其他线程中被调用. 在该方法中你应该回到主线程然后调用completion handler去触发AppDelegate中的application:​handle​Events​For​Background​URLSession:​completion​Handler:​方法.

常量

参考文档

NSURLSession - Foundation
WWDC 2013 - Session 204 - What's New with Multitasking
WWDC 2013 - Session 705 - What's New in Foundation Networking
WWDC 2015 - Session 711 - Networking with NSURLSession
WWDC 2016 - Session 711 - NSURLSession: New Features and Best Practices

上一篇 下一篇

猜你喜欢

热点阅读