iOS网络协议栈原理(一) — URLSession简介
URLSession是Apple iOS 系统中的官方网络库, 第三方库例如, AFNetworking
, Alamofire
, 以及react-native
的网络库RCTNetwork
都是基于官方的URLSession
.
因此弄清楚URLSession
的核心架构, 对于在iOS平台做开发非常重要, 这套架构一般称为iOS URL Loading System
1. iOS URL Loading System
的HTTP关联类
网上有大量的使用URLSession
发起网络请求的文章, 这里就不深入了. 这里只举几个比较有代表性的例子, 主要为了引出URLSessiohttp 404n
的关联类对象:
func testSharedURLSession() { //1. 创建URL let url = URL(string: "https://www.baidu.com/") //2. 构造 http request var request = URLRequest(url: url!) request.setValue("value", forHTTPHeaderField: "HeaderFiled") request.httpBody = Data() //3. 使用 http request 通过Session构造 task let task = URLSession.shared.dataTask(with: request) { data, response, error in // 请求回调结果 } //4. task 正式发起请求 task.resume() } func testCustomURLSession() { // 1. 创建 Session 配置对象, 所有通过 Session的创建的 Task, 会有一些公用属性 let config = URLSessionConfiguration.default config.timeoutIntervalForRequest = 5 config.httpAdditionalHeaders = ["header2": "svalue2", "header3": "svalue3", "header4": "svalue4"] // 2. 使用 config 创建 Session // 可以设置 session的 delegate 和 delegateQueue let session = URLSession(configuration: config, delegate: nil, delegateQueue: OperationQueue.main) // 3. 通过 session 创建 Task(包装Request) let url = URL(string: "https://www.baidu.com/") let request = URLRequest(url: url!) let task = session.dataTask(with: request) { data, response, error in // 请求回调结果 } // 4. 真实启动任务, 发起请求 task.resume() }
基本上iOS URL Lo成员变量和静态变量的区别ading system
会与如下各种类型相关:
-
HTT成员变量和局部变量区别P Request
:URL
+URLRequest
-
HTTP Response
:URLResponse
,HTTPURLResponse
- Task工厂类:
URLSessi缓存on
+URLSessionConfiguration
- 不同功能的Task:
URLSessionTask
,URLSessionDataTask
,URLSessionUploadTask
,URLSessionDownloadTask
- 传输层关联内容:
URLProtocol
- 缓存相关:
URLCache
- Cookie相关:
HTTPCookie
,HTTPCookieStorage
- 鉴权相关:
URLAuthenticationChallenge
,URLCredential
,URLCredentialStorage
,URLPrhttp代理otectionSpace
- URLSession底层真实的网络请求库:
curl
2. Task任务工厂URLSession
, 以及_成员变量用于描述对象的特征TaskRegistry
在上面的Demo中能看到, 不论是使用URLSession
的sh缓存是什么意思ared
实例, 或者自定义创建一个URLSession
的实例, 最终需要通过以下3个方法创建URLSession缓存视频怎样转入相册Task
的实例:
func dataTask(with ..实例化对象是什么意思.) -> URLShttp代理essionDataTa缓存视频合并appsk
func uploadTask(with ...) -> URLSessionUphttpclientloadTask
func downloadTask(with ...) -> U缓存的视频在哪RLSessionDownloadT实例化servlet类异常ask
简单来说, URLSession
的实例, 就是一个 Tahttpwatchsk工厂
, 它可以通过不同的方法, 创建不同的URLSessionTask
!!!
我们可以通过实例化URLSession
时, 通过URLSession成员变量和局部变量Configuration
给这个工厂做一些通用配置, 这些配置可以参考URLSe成员变量用于描述对象的特征ssionConfiguration
的属性和方法, 基本上有:
-
requestChttps认证achePolicy
: 工厂产生的Task关联的Request的通用Cache策略 -
timeout
: 各种timeout, 请注意, 这些timeout大多数是在URLSessionTask
这个对象中缓存的视频在哪维护的 -
httpAdditionalHeaders缓存视频合并app
: 通用的HTTP Header
-
httpCookieStorage
: 统一的Cookihttp://192.168.1.1登录e管理对象 -
urlCredentialStorage
: 鉴权复用对象 urlCache
-
protocolClasses
: 本地真实的数据代理类型!!!!!!
最终的Task携带的配置, 会依赖URLRequest参数
结合URLSession工厂实例化servlet类异常
的两者的配置而产生, URLRequest
的配置会覆HTTP盖工厂的默认配置.
每个URLSession
实例都有一个taskRegistry成员变量
, 它是一个内部类 — _TaskReg成员变量有没有默认值istry
, URLSession实例
用它来持有自己生产的URLSessionTask
, 具体的实现方式可以参考如下代实例化对象是什么意思码:
open class URLSession: NSObject { ... internal let taskRegistry = URLSession._TaskRegistry() // 管理当前 Session 发起的 所有 Task /// 任务管理中心 class _TaskRegistry { // 真实的数据业务的回调 /// Completion handler for `URLSessionDataTask`, and `URLSessionUploadTask`. typealias DataTaskCompletion = (Data?, URLResponse?, Error?) -> Void /// Completion handler for `URLSessionDownloadTask`. typealias DownloadTaskCompletion = (URL?, URLResponse?, Error?) -> Void // 具体的 task 真实的行为 - 可能有几种! /// What to do upon events (such as completion) of a specific task. enum _Behaviour { /// Call the `URLSession`s delegate case callDelegate /// Default action for all events, except for completion. case dataCompletionHandler(DataTaskCompletion) /// Default action for all events, except for completion. case downloadCompletionHandler(DownloadTaskCompletion) } // key 是 task 的Identifier fileprivate var tasks: [Int: URLSessionTask] = [:] fileprivate var behaviours: [Int: _Behaviour] = [:] // TaskRegistry 完全清空以后的回调 fileprivate var tasksFinishedCallback: (() -> Void)? func add(_ task: URLSessionTask, behaviour: _Behaviour) { ... } func remove(_ task: URLSessionTask) { ... } var allTasks: [URLSessionTask] { return tasks.map { $0.value } } } ... }
我http://www.baidu.com们知道URhttps域名LSessionTask
的回调方式分成两种以下两种, 具体使用哪种也是在_TaskRegistry._Behaviour枚举
中进行维护的:
- completionBlock
- delegate
那么, 这些回调方法是如何关联并管理的呢?
3. URLSession成员变量和局部变量
创建 URLSessionTask
并使用taskRegistry
管理的流程
简单看一HTTPS下这个过程:
open class URLSession: NSObject { ... internal let workQueue: DispatchQueue = DispatchQueue(label: "URLSession<(identifier)>") /// URLSession 的关键 API open func dataTask(with request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask { return dataTask(with: _Request(request), behaviour: .dataCompletionHandler(completionHandler)) } func dataTask(with request: _Request, behaviour: _TaskRegistry._Behaviour) -> URLSessionDataTask { // 1. 判断 session 状态是否 - fatalError guard !invalidated else { fatalError("Session invalidated") } // 2. 使用工厂方法配置 request let r = createConfiguredRequest(from: request) // 3. 为初始化创建 task 做准备, 构造 taskIdentifier 作为唯一id let i = createNextTaskIdentifier() // 4. task 创建方法 -> 注意 task会持有session !!! let task = URLSessionDataTask(session: self, request: r, taskIdentifier: i) // 5. taskRegistry 管理 task + behaviour workQueue.async { // 做法很简单, task.Identifier 是key // 管理 tasks, behaviours self.taskRegistry.add(task, behaviour: behaviour) } return task } // 内置一个枚举内容 - URLSession 会关联两个内容 -> 要不直接使用 URL/Request enum _Request { case request(URLRequest) case url(URL) } // 通过 外部request + configure 构造真实的 URLRequest func createConfiguredRequest(from request: URLSession._Request) -> URLRequest { let r = request.createMutableURLRequest() // 使用内置的 configuration 配置 request return _configuration.configure(request: r) } }
通过以上核心代码, 我们能得到如下关键信息:
-
URLSession
拥有一个serial DispatchQueue
—workQueue
, 左右针对taskRegistry
的操作, 都是在这个串行队列缓存文件夹名称中完成, 保证线程安全 - 内部拥缓存是什么意思有一个枚举类
_Request
, 使用它封装对外API传入的URL/URhttp协议LRequest
- 不论最终的回调是
delegate or completionHandler
, 都会在内部成员变量封装成统一的behaviour枚举
-
URLSessionDataTask
实际是通过request
创建的!!!
一句话总结: URLSession
是一个生产URLSessionDataTask
的工厂!!!
评论(0)