【重读iOS】网络请求2:应用

  1. 基础知识
  2. HTTP基础知识(状态码,请求方法,请求头,cookies)
  3. socket/webSocket
  4. 系统请求库和开源请求库
  5. 缓存系统(LRU LFU)
  6. 应用场景:文件上传,断点续传,加密和签名。
  7. 特殊请求:流媒体协议,IM长连接

系统库

以前是NSURLConnection,现在已经改版成NSURLSession为核心了。架构如图:

【重读iOS】网络请求2:应用_第1张图片
image

即session控制多个请求,每个请求对应为一个SessionTask,然后使用SessionConfiguration来配置session的性质。

task有4种:dataTask,downloadTask,streamTask,然后uploadTask继承于dataTask。

构建task之后通过resume方法开启,然后通过session的delegate方法回调处理请求的种种状况。

1. session构建
  • 自建还是用share
  • config的配置细节,不用default奔溃
2. 权限验证

HTTPS知识

  • App Transport Security (ATS)

验证服务器的证书正确性:

//默认是让系统做默认处理
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
    NSURLCredential *credential = nil;
    
    if (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust) {
        SecTrustRef secTrust = challenge.protectionSpace.serverTrust;
        
        SecTrustResultType result = 0;
        SecTrustEvaluate(secTrust, &result);
        
        if (result == kSecTrustResultProceed ||
            result == kSecTrustResultUnspecified) {
            
            credential = [[NSURLCredential alloc] initWithTrust:secTrust];
            //只有证书验证通过并且构建成功时信任证书
            if (credential) {
                disposition = NSURLSessionAuthChallengeUseCredential;
            }
        }else{
        //证书验证失败,取消信任
            disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
        }
    }
    
    completionHandler(disposition, credential);

证书分为从CA签发的证书跟自建证书两种,证书都是一级级签发的,形成一个证书链。从CA签发的证书和自建证书的区别就是前者的根证书是公开已知的,操作系统在安装过程中会默认安装一些受信任的CA机构的根证书,所以如果是第一张,系统自身就可以完成这个验证过程。

如果是自己签发的证书,系统是不认识的,要自定义认证过程。这部分可参考AFNetWorking的处理:

//替换为自己本地指定的证书
SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);
//验证证书
if (!AFServerTrustIsValid(serverTrust)) {
    return NO;
}
3. 接收数据3件套

didReceiveResponse: didReceiveData: didCompleteWithError:

4. downloadTask
  • 它会把返回内容下入到一个临时文件里,结束的回调里提供这个文件地址
  • 支持断点续传操作:
[downloadTask cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
       //使用这个方法取消,获取到用于重启的数据,保存下来
   }];
 ...
 //之后使用这个resumeData重新构建下载任务
 NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithResumeData:resumeData];

从这两个特性可知:downloadTask是用来应对大的文件传输的,在这种应用环境下可考虑使用。

设计上,使用resumeData就可以重启下载,说明这个数据里至少保存了URL信息,所以猜测它的设计里,这个数据可能并不是下载的数据,而是一系列的描述信息,如URL、证书认证、之前下载的文件地址、已下载字节大小等。

可惜这里是不透明的NSData类型,断点续传的功能完全可以自己设计,还可以更好的控制下载文件位置、缓存、数据模型等。比如超级大图要求下载一部分就立马显示出来,这种需求就需要在过程中就获取数据,而这里downloadTask的delegate方法只是提供了数据大小却没有提供数据位置。

断点续传的实现

  • 请求头加入Range,如Range:0-2000
  • 返回头会相应的填入Content-Range:0-1000/24304,表示返回的返回和总大小。
  • 考虑到文件可能被修改,可加入If-Range,值为文件的标识(Etag)或者Last-Modified的值,服务端根据这个值判断文件是否修改了,未修改,返回指定的部分数据(206),已修改,返回整体数据(200)。
5. uploadTask和文件上传协议

使用系统的上传方法:

uploadTaskWith...系列有3个方法,抓包查看,使用uploadTaskWithRequest:fromFile:方法的请求类型为:Content-Type application/octet-stream,而另外两种为Content-Type application/x-www-form-urlencoded

对后台处理似乎没什么区别,都是从数据流里读取然后写入文件。系统的上传有很鸡肋:1. 没法同时传多个文件 2.没法同时传入其他的参数。

如果你要做这些,就要自己去构建request,那么这个uploadTask其实已经没什么作用了,就是其他类型的task照样能完成任务。

自定义上传方式:如果要解决掉上面说的两个缺点,要使用multipart/form-data的方式,具体参考iOS里实现multipart/form-data格式上传文件,或者直接使用第三方库,AFNetWorking内部就是使用这种方式上传文件。

6. 断点续传
  • 使用Accept-Ranges判断后台是否支持断点续传,支持时值为bytes,不支持为none
  • 客户端请求时设置Range:bytes=20-150来指定请求的数据范围,bytes是单位,注意这个开始结束的索引是从0开始算的,然后10-表示从10到结束所有内容。
  • 服务端返回时,使用Content-Range:bytes10-200/1340,10为开始,200为结束,1340为总长度。
  • 返回时还要修改:1. http状态码为206(部分数据) 2.Content-Length要修改为部分数据的长度,而不是总长度,否则客户端会一直等待数据传完,但实际已经结束,最后会导致超时错误。

断点续传还有一个好处是可以多线程下载数据,把大文件切割为多个部分,分多个线程分别请求,然后在本地拼接。

7. 缓存系统(LRU LFU)

参考缓存策略之LRU和LFU

你可能感兴趣的:(【重读iOS】网络请求2:应用)