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
的工厂!!!