本文主要是看了
- WWDC 2015 - Session 711 - Networking with NSURLSession
- WWDC 2016 - Session 711 - NSURLSession: New Features and Best Practices
做出的翻译和总结, 文字/视频链接如下 :
- wwdc2015/711 : Networking with NSURLSession
- wwdc2016/711 : NSURLSession: New Features and Best Practices
App Transfer Security
iOS9新特性. 核心是防止用户的个人隐私数据被意外泄漏. 加强默认Configuration的安全性.
HTTP
以前HTTP请求是明文传输(clearText)的, 别人拦截请求之后就能获取所有信息.
HTTPS
HTTPS通过在TCP于HTTP层之间加入一层SSL/TLS层, 能做到 :
- Encryption : 信息加密传输, 别人看不懂
- Integrity : 保证数据完整性, 即第三方篡改后能检测出来
- Authentication : 身份验证, 防止第三方伪造通信
HTTPS in iOS
苹果希望所有所有请求都使用HTTPS来加强安全性, 但是也有以下几种例外(以下全部可以在info.plist
文件中配置) :
- 全局HTTPS请求, 但是某个域名采用HTTP请求
NSAppTransportSecurity
NSAllowsArbitraryLoads = NO // 默认为NO, 这一行不要也行
NSExceptionDomains
"media.example.com"
NSExceptionAllowsInsecureHTTPLoads = YES - 全局HTTP请求, 但是某个域名采用HTTPS请求
NSAppTransportSecurity
NSAllowsArbitraryLoads = YES
NSExceptionDomains
"secure.example.com"
NSExceptionAllowsInsecureHTTPLoads = NO - 全局HTTP/HTTPS(任意)请求
NSAppTransportSecurity
NSAllowsArbitraryLoads = YES
使用NSAllowsArbitraryLoads
可以检测app中加载失败是不是因为ATS的原因.
CFNetwork中用于网络诊断的环境变量CFNETWORK_DIAGNOStICS = 1
: 所有加载失败的URL都能被确定是URL错误还是底层TLS错误. 这样能进一步排查错误.
下面为ATS新增的属性
NSAllowsArbitraryLoadsInWebContent : UIWebView会无视app的其他协议, 加载HTTP的内容. 仅限于UIWebVie对象, 其他对象还是对遵循安全协议.
NSRequiresCertificateTransparency : 保证证书的合法性.
NSURLSession新特性
NSURLSession将全面支持HTTP/2 !
为什么需要HTTP/2
由于HTTP/1.1存在许多问题 :
-
单路连接 请求低效
- 每个TCP连接只能对应一个HTTP请求, 每个HTTP请求只请求一个资源, 浏览器只能通过建立多个TCP连接来解决, 但是由于文本协议开销, 缺乏头压缩, 意味着对客户端和服务器的要求更高且性能更低下
- 使用HTTP管道, 但是HTTP管道并不适用于所有的服务器或网络, 实际上在很多浏览器上是被禁止的. 事实上效率并不高.
- HTTP只允许由客户端主动发起请求
- 意味着缺少预加载的功能
- HTTP头冗余
- 每次发送HTTP请求都有一堆重复的header value
了解HTTP/2
HTTP/2 有什么改进 :
- 一个TCP连接能发起多个HTTP请求
- 实现完全多路复用, 意味着一个新的请求不用等到上一个请求得到响应之后再发出
- 有请求优先级, 所以能把重要的资源优先提供给拥有更高权限的客户端.
- 使用二进制分帧, 使得数据的处理和解析速度更快.
- 使用
HPACK
头压缩技术- 使用一个静态表和一个动态表
- 静态表包含最常用的HTTP信息头, 并且不可修改
- 包含在静态表中的信息头可以动态添加到动态表中, 动态表中的信息头通过指针引用静态表中的信息
- HTTP/2自动支持HTTPS, 意味着HTTP/2是安全的
- 支持服务器自推送
HTTP/2 多路复用
Q : HTTP/2多路复用是如何队首阻塞的问题的?
A : 在HTTP/1.1的时候曾试过采用HTTP pipelining (HTTP 管道/流水线 技术)能实现同一TCP连接中不用等待旧请求的响应就可以发送新请求. 但是HTTP pipelining有个致命的缺点 : HTTP响应仍然是按照请求的顺序依次收到.
HTTP/2 多路复用+请求优先级, 发送的时候还是依次发送请求, 但是与此同时我们同时得到了回复, 同时, 更高优先级的请求我们得到以及发送给客户端的速度更快, 如下图所示 :
服务器自推送
在HTTP/1.1时代, 我们没有服务器自推送, 只能发一个请求, 得到响应之后再发第二个请求... 延迟可想而知 :
而在HTTP/2时代, 我们有了服务器自推送, 服务器会把相关联的数据全部push给我们 :
性能马上强了一大截, 我们再也不用像以前那样苦苦等待了.
并且这功能已经内嵌在NSURLSession中, 我们不需要写任何一行代码来支持其实现.
iOS中适配HTTP/2
NSURLSession已经自动支持HTTP/2, 客户端不需要额外写任何代码. 只需要一台支持HTTP/2通信的服务器即可. 哎呀, 要是没有怎么办, 也没关系, 看下面 :
- 服务器支持HTTP/2
- NSURLSession自动使用HTTP/2进行通信
- 服务器不支持HTTP/2
- NSURLSession自动使用HTTP/1.1或其他更优的协议
我们一句代码, 判断都不用写, 省事又省心.
SPDY
这里说句题外话, 虽然SPDY并不在该Session中提及, 但是与HTTP/2有异曲同工之处, SPDY在之前被认为是未来的HTTP/2, 所以这里说一下.
SPDY是Google开发的基于TCP的应用层协议, 通过头部压缩, 多路复用和优先级来缩短网页的加载时间和提高安全性. 可以说是HTTP/1.1的一个优化版.
- 多路复用, 请求优化
- 支持服务器推送技术
- SPDY压缩了HTTP头
- 强制使用SSL传输协议
更多有关SPDY的详情请见这里
惊 ! iOS9开始苹果弃用NSURLConnection, 不再维护NSURLConnection, 所有有关Networking的新API只会在NSURLSession上更新.
NSURLSession的新功能
cookie 共享
增加sessionCookieGroup以实现app与其扩展(例如通知中心等等)之间cookie的共享.
let ident = "group.mycompany.mygroupname"
let cookieStorage = NSHTTPCookieStorage.sharedCookieStorageForGroupContainerIdentifier(
identifier: ident)
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
config.HTTPCookieStorage = cookieStorage
let session = NSURLSession(configuration: config)
NSURLSessionStreamTask
以前我们使用NSInputStream/NSOutputStream
来进行一些非HTTP的连接, 例如利用TCP连接一台远程的服务器等等, 现在我们有了NSURLSessionStreamTask
让我们更简单地实现以上功能.
NSURLSessionStreamTask的特性 :
- 更轻松地使用TCP进行通信
- 替代NSInputStream/NSOutputStream, 提供更优的API
- 异步读写API
- 内置强大的支持功能, 能自动通过HTTP代理, 连接一个远程服务器.
- 轻松转换成NSStream
NSURLSessionTaskMetrics
对发送请求/DNS查询/TLS握手/请求响应等各种环节时间上的统计. 更易于我们检测, 分析我们的请求缓慢到底是发生在哪个环节, 并对此进行优化提升我们APP的性能.
下面开始介绍NSURLSessionTaskMetrics
的属性
Property
taskInterval : 任务从开始到结束总共用的时间
redirectCount : 任务重定向的次数
transactionMetrics : 在任务执行期间产生的每个请求/响应事务, 它是一个装着许多
NSURLSessionTaskTransactionMetrics
对象的数组
NSURLSessionTaskTransactionMetrics
property
request and response : 请求和响应
-
networkProtocolName : 网络协议名称
- http/1.1
- http/2
- spdy/3, spdy/3.1
isProxyConnection : 是否连接代理服务器
isReusedConnection : 是否允许重连
-
resourceFetchType : 描述本次加载的类型, 枚举类型
- networkLoad : 网络加载
- localCache : 本地缓存
- serverPush : 服务器自推送
-
Connection Establishment and Transmission
fetchStart : 开始发起请求.
domainLookupStart : 发送DNS请求, 域名->IP地址
domainLookupEnd : DNS请求完成, 拿到IP地址
-
connectStart : 与远程服务器开始建立TCP连接
- secureConnectionStart : HTTPS的TLS握手开始
- secureConnectionEnd : HTTPS的TLS握手完成
connectEnd : 与服务器建立起了TCP连接
requestStart : 开始传输HTTP header第一个字节的时间(远程/缓存)
requestEnd : HTTP最后一个字节传输完成的时间(远程/缓存)
responseStart : 从服务器得到数据(远程/缓存)
responseEnd : 从服务器接受完最后一个字节的数据(远程/缓存)
另外, 需要注意的是, 如果请求命中了cache, 上述很多值会为nil.
API
NSURLSessionTaskDelegate
代理中新增一个方法- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics
, 当收集完成的时候就会调用该方法.
我们只需要实现代理的这个方法就能在这里做统计, 输出等等操作.
Something else
苹果不再支持RC4加密.
以上图片均来自官方WWDC中的PDF.