负责协调一组相关网络数据传输任务的对象。
概论
URLSession
和一系列相关的类,提供了一个API,来下载内容。这个API提供了一组丰富的代理方法来支持认证并给你的App一种在后台下载的能力,当你的App不在运行,或者在iOS系统中,你的App处于休眠状态的时候。
URLSession
类支持data
file
ftp
http
和 https
的url,支持代理服务和SOCKS通道。并被配置到用户的系统中
URLSession
支持HTTP/1.1,SPDY,HTTP/2协议。HTTP/2的支持被描述在RFC 7540,并且需要一个服务支持,无论是ALPN还是NPN
你同样可以用URLProtocol
为你自定义的网络协议和URL schemes提供支持
Important
URLSession
API通过一种灵活的方式调用许多类一起工作。如果你只看URLSesson
的文档可能不能一下子就看出来。在使用这个API之前你应该阅读URL Session Programming Guide这篇文章,来获得一个总体的了解。为了弄清楚这些类是怎么互相合作的。
使用URLSession API,你的App可以创建一个或者更多的session,每个session负责协调一组数据传输任务。例如,如果你正在写一个网络部分,你的App可能要为每个tab或者window创建一个session,或者一个session用来交互,另一个用来进行后台的下载。在每个seesion中,你的App添加一些任务,每个任务代表了一个特定URL的请求。
每个被指定了URL的session的任务,共享一个session configuration object,这个object定义了网络链接的行为,例如,对于同一个host最大的并发链接数量;是否允许链接蜂窝网络等等。session的部分行为被确定,当你创建一个configuration object的时候:
- 一个最简单的请求,是通过一个shared session(不需要configuration object)来进行的。它不像你自己创建的session那样可以自定义。但是如果你的要求很有限,这是一个很好的开始。你通过shared
来访问这个session。详情见这个方法关于它的局限性的相关讨论。 - 默认的session和shared session很像,除非你做了更多的自定义设置。但是能让你通过使用delegate获取不断增加的数据。在URLSessionConfiguration
类里面你可以通过调用 默认的default
方法创建一个默认的session configuration -
Ephemeral session和default session很像,但是不能把caches、cookie、和credential(证书)写到磁盘上。在URLSessionConfiguration
类里面你可以通过调用 默认的ephemeral
方法创建一个默认的session configuration。 -
Background session让你在后台执行一个上传和下载任务,你可以通过调用URLSessionConfiguration
类的backgroundSessionConfiguration(_:)
方法,创建一个background session。
session configuraion object同样包含一个URL cache和存储cookie对象的reference。在request和处理responses的时候可能会用到,这取决于configuration和request type
在一个session中的很多任务共享公共的delegate。当不同的事件发生的时候,会让你获得一些消息。例如,认证失败、当从server获取的数据、数据准备被缓存等等。对于所有的后台下载和上传。你必须要提供一个代理者,遵循URLSessionDownloadDelegate
这个协议。如果你不需要delegate提供的消息,你可以在创建session的时候,delegate参数传入nil。
Important
session object强引用delegate,直到你的App退出,或者明确的让session失效。如果你不release session,你的App在退出前都存在一个内存泄露的问题。
通过session,你创建一个任务来上传数据到server,然后从server接受数据。无论是作为磁盘上的数据,还是在内存中一个或者更多的NSData
对象。URLSession API提供了三种类型的任务:
-
Data tasks 通过使用NSData
对象来发生和接受数据。Data task主要针对于简短且经常和server交互的请求。 - Upload tasks和data task很相似,但是它一般以文件的形式发送数据。当App不在前台的时候,支持后台上传。
-
Download tasks以文件的形式接受数据,当App不在前台的时候,支持后台的上传和下载.
和大多数网络请求API一样,URLSession API是异步操作。它返回数据通过以下两种方式中的一种,这取决于你调用的方法: - 当数据传输成功或者发生错误的时候,一个完成的closure被调用。
- 当数据传输成功或者发生错误的时候,一个完成的delegate被调用。
除了向代理传输数据,URLSession API提供了status(状态)和progress(过程)的属性。你可以通过这些来判断,如果你需要基于任务当前状态做一个程序上的决定的时候。
URL session 同样支持canceling(取消)、restarting(重新开始)、resuming(开始或继续)、suspending(暂停)一个任务。并且提供一种继续已经暂停、取消、下载失败的任务的能力。
URL Session类的结构(URL Session Class Hierarchy)
NSURLSesson API用下面的类组成(展示了一些内嵌的父子类关系)
-
URLSession
—— 一个session对象 -
URLSessionConfiguration
—— 一个configuration对象,当初始化session的时候要使用 -
URLSessionTask
—— session中task的父类。-
URLSessionDataTask
—— 以NSData对象的形式接受URL中内容的task-
URLSessionUploadTask
—— 上传文件的task,以NSData对象的形式接受URL中内容的task
-
URLSessionUploadTask
URLSessionDownloadTask
—— 以磁盘上临时文件的形式接受URL中内容的taskURLSessionStreamTask
—— 建立TCP/IP连接的任务
-
另外,NSURLSesson API 提供了五个协议。这些协议定义了你app能够实现的代理方法,提供了对于sesson和task的行为更加细粒度的控制。
- URLSessionStreamTask —— 处理sessin层的事件
- URLSessionTaskDelegate —— 处理所有任务类型通用的task层的事件
- URLSessionDataDelegate —— 处理特殊数据和上传任务的task层事件
- URLSessionDownloadDelegate —— 处理特殊下载任务的task层事件
-
URLSessionStreamDelegate
—— 处理具体stream任务的task层事件
最终,NSURLSession API使用大量的类,同时这些类也被其他的API使用例如NSURLConnection和NSURLDownload。这些类中的一些会被共享使用:
-
NSURL
—— 包含URL的类 -
NSURLRequest
—— 包装了和URL 请求相关的元数据,包含,URL、请求方法等。 -
URLResponse
—— 包装了server对于请求响应的元数据,包含,MIME的类型和长度。-
HTTPURLResponse
—— 添加对于特定HTTP请求另外的元数据,例如,response header(响应头)
-
HTTPURLResponse
-
CachedURLResponse
—— 包装了一个URLResponse object,同时还有真实的server的response的body data,为了用来缓存。
认证和自定义TLS(Authentication and TLS Customization)
当一个server请求认证或者在TLS认证提供证书的时候,URL session调用它代理的方法,允许你以一种自定义的方式处理认证或证书校验。这些方法的调用依赖是否你正在处理一个特殊的任务的变化或者一个session范围内的改变。下面的表格展示了这两者的不同:
具体任务发生改变,session调用代理的urlSession(_:task:didReceive:completionHandler:)方法。
范围内的session认证发生改变,如果urlSession(_:didReceive:completionHandler:)方法被实现的话,session调用它,否则,调用代理的urlSession(_:task:didReceive:completionHandler:)方法。
如果你没有实现这些方法,当请求需要一个客户端认证的时候,URL session尝试如下方式进行认证:
- 如果可以的话, 使用RUL request部分所提供的认证信息
- 通过在mac用户的钥匙串或者iOS系统中app的钥匙串中寻找的网络密码和证书,
接着,如果认证不通过,或者server拒绝这个证书,没有认证的连接会继续进行。对于HTTP和HTTPS请求,链接失败会返回一个适当的状态码,许多请求都提供了二选一的内容(例如公共版本的私有地址)。其他的URL类型例如FTP,链接失败就以链接失败结束,没有状态码。
Note
Kerberos认证是以一种透明的方式来处理的。这里描述的代理方法不适用于Kerberos 认证。
App传输安全(App Transport Security (ATS))
从iOS9.0和OS X v10.11开始,一个新的安全特性叫做App Transport Security(ATS)被启用。默认的所有的HTTP链接要用NSURLSession建立。ATS要求HTTP链接使用HTTPS(RFC 2818)。
更多信息,见在 Information Property List Key Reference中的 NSAppTransportSecurity。
使用URL Session(Using an URL Session)
使用NSURLSession类建立请求:
- 创建一个session configuraton。对于background session,这个configuration必须包含一个唯一标志符。存储标志符,如果你的App crash或者被停止,或暂停,使用它来重新关联session。
- 创建一个session,指定一个configuration对象和一个可选的代理。
- 创建一个task object包含一个session。每个task代表了一个资源的请求。task object是 URLSessionTask的子类 —— URLSessionDataTask, URLSessionUploadTask, 或 URLSessionDownloadTask,这取决于你想要完成的行为。
每个任务从一个初始的状态开始。在你的app调用task的resume()
方法的时候,它开始下载具体的任务内容。
在开始一个任务之后,session调用它delegate上的如下方法:
- 如果和server的初次握手需要链接层的变化,例如:SSL客户端认证(SSL client certificate),NSURLSession调用urlSession(_:task:didReceive:completionHandler:)或者urlSession(_:didReceive:completionHandler:)这两个代理方法中的一种。正如上面在Authentication and TLS Customization说明的。
更多关于NSURLSession的认证代理方法的信息请看URL Session Programming Guide - 如果task的数据是从一个stream提供的,NSURLSession object调用代理的urlSession(_:task:needNewBodyStream:)这个方法,来获得NSInputStream object。它为新的request提供了数据body。
- 在初始化body内容上传到server的期间,代理周期性的接受urlSession(_:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:)这个方法的callback。来监听数据的上传过程。
- server发送response
- 如果response指出认证是必须的,session调用它自己的代理方法urlSession(_:task:didReceive:completionHandler:)。回到第二步。
- 如果response是一个HTTP重定向response,NSSession对象调用它自己的代理方法urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)。这个方法会调用被提供的完成handler,通过一个被提供的NSURLRequest object(为了跟随重定向),一个新的NSURLRequest object(为了重定向到不同的URL),或者什么都不提供。
- 如果你决定重定向,回到第二步
- 如果代理没有实现这个方法,重定向尝试直到重定向的最大次数
- 对于一个通过调用 downloadTask(withResumeData:)
或者downloadTask(withResumeData:completionHandler:)
方法创建的download task,NSURLSession调用代理的urlSession(_:downloadTask:didResumeAtOffset:expectedTotalBytes:)通过使用新的task object。 - 对于data task,NSURLSesson object调用代理的urlSession(_:dataTask:didReceive:completionHandler:)方法。决定是否把data task转换成download task,接着,调用完成的callback继续接受数据或者下载数据。
如果你的app选择把data task转换成download task,NSURLSession调用代理的 urlSession(_:dataTask:didBecome:)方法,通过传入新的download task作为参数。在调用之后,代理者不再接受来自data task的callback,并开始接受来自downlod task的回调。 - 在从server传输数据的时候,代理周期性的接受一个task层的callback来监听传输进度。
对于一个data task,session调用代理的urlSession(_:dataTask:didReceive:)
方法,得到接受到的实际数据。
对于一个download task,session调用代理的urlSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:)通过传入bytes的,成功的写到磁盘上。如果用户让你的app暂停下载,通过cancel(byProducingResumeData:) 方法取消任务。
之后,如果用户要求你的app开始下载任务,传入被返回的重新开始的数据到方法 downloadTask(withResumeData:)或者方法downloadTask(withResumeData:completionHandler:)来创建一个新的下载任务来继续下载。(回到第一步) - 对于一个data task,NSURLSession object可能调用代理的urlSession(_:dataTask:willCacheResponse:completionHandler:)方法。你的app要做出回应是否允许缓存。如果你没有实现这个方法,将会默认的使用在session configuration里面的缓存策略。
- 如果response是多部分编码,session可能再次调用代理的didReceiveResponse方法在调用更多次didReceiveData方法之后。如果这种情况发生了,回到第八步。
12.如果下载任务成功完成,接着,NSURLSession对象调用task的urlSession(_:downloadTask:didFinishDownloadingTo:)方法通过传入一个临时文件地址。你的App必须在代理方法return之前,从这个文件里读取response数据或者把数据移动到一个固定的地址。
13.Task完成的时候,NSURLSession调用代理的urlSession(_:task:didCompleteWithError:)方法,通过传入一个error object或者nil(如果任务成功完成)。如果download任务被重启,NSError对象的userInfo字典会包含一个以 NSURLSessionDownloadTaskResumeData为key的值。你的App需要传入这个值到方法 downloadTask(withResumeData:)
或者downloadTask(withResumeData:completionHandler:)
中来创建一个新的下载任务。来继续之前的下载。
如果任务不能被重启,你的App应该创建一个新的download task ,重新开始数据的传输。
无论哪种情况,如果你的传输因为一些原因失败了不是因为server的问题,回到第三步(创建并重新开始一个task)
Note
NSURLSession不能通过error parameter抛出server的error。唯一你代理能接收到的error是客户端这边的error。例如,不能解析主机域名,或链接到主机。错误码被描述在URL Loading System Error Codes中。
server的error code在NSHTTPURLResponse object中通过HTTP 的状态码(status code)的形式来抛出。更多的信息,请阅读HTTPURLResponse和URLResponse的相关文档。
14.如果你不再需要session,你应该使它无效,通过调用invalidateAndCancel()或者finishTasksAndInvalidate()
在使session无效之后,当所有未结束的task被cancel或结束之后,session调用代理的urlSession(_:didBecomeInvalidWithError:)方法。当代理方法返回,session会release了它持有的代理的强引用。
如果你的App在下载过程中被cancel,NSURLSession object会调用代理方法urlSession(_:task:didCompleteWithError:),就相当于一个error发生了。
后台传输的顾虑(Background Transfer Considerations)
因为重启你的App(或者用户relaunch)的代价是非常昂贵的,一些特性是无法在后台的session里生效的。
- 在数据传输过程中,Session必须为传递的事件提供一个代理。因为你的app可能quit,被重新加载。完成的handler block是不支持的。(对于上传和下载,这些代理行为和在传输过程中是一样的)
- 只有HTTP和HTTPS可以被支持,其他内置的网络协议不支持,自定义的网络协议也不支持。
- 只有上传和下载task支持,data task不支持。
- 系统范围内后台最大数据并发传输受到限制。
- 重定向一直会被紧随。
- 如果失败,后台task会被取消,为了满足系统吞吐量限制。换句话说,如果一个长时间运行的任务不再接受或者发送足够的数据在它的持续时间里,为了之后的重新开始,它可能会被cancel。因此,如果可能的话,让数据重新开始是很重要的。
- 当你的App在后台的时候,如果一个后台的传输被创建,这个task会作为discretionary被对待。换句话说,它的行为和configuration object的isDiscretionary为true的session中的task一样。
如果以上要求和你App的需求冲突,你同样可以下载远程资源到文件中,用non-background的sesson。如果你这样做了,当用户把你的App退到后台或者退出macOS App,会通过cancel(byProducingResumeData:)暂停所有活动中的下载,当用户回到你的App,再继续下载。如果你的App在你获得resume data的时候就已经停止,你将不能继续之前的下载。
Note
Background session优化了较少数量的大量资源的传输,可以在需要的时候继续传输。如果可以,你应该尽可能的优化server的传输,避免这种情况的发生。
- 创建到终端的请求。发送或者接受zip、tar类型的文件而不是进行少数个别分开的调用。
- 创建到终端的请求。为了复制,发送或接收不断增长的差异数据。
- 创建到终端的请求返回一个上传的identifier,这可以被用来跟踪和继续一个到server的数据传输
- 添加一个中间的网络service,代理请求到网络service。为了减轻上述所说优化项的负担。
NSCopying行为(NSCopying Behavior)
Session和task object遵循NSCopying 协议,如下:
- 当你的App拷贝session或者task object,你可以获得一个相同的返回对象(浅拷贝)。
- 当你的App拷贝一个configuration object,你获得一个新的copy,你可以独立的修改。
线程安全(Thread Safety)
URL Session API是线程安全的。你可以自由的创建session和task在任何线程中。当你的代理方法调用被提供的完成回调,它会自动的把调用分配到正确的队列里面。
Warning
你的 urlSessionDidFinishEvents(forBackgroundURLSession:)
session代理方法可以在一个辅助线程中被调用。但是,在iOS中,你的方法的实现可能需要调用在application(_:handleEventsForBackgroundURLSession:completionHandler:)方法里被你所提供的完成handler。你必须要在主线程里面调用该方法。
校验:LinkRober
本文译自URLSession
有翻译不准确的的地方或有待改进的地方欢迎指正🙏🙏🙏