网络编程(三)

343 阅读13分钟

一. NSURLSesscion概述

1.0 简介

2013 年的 WWDC 大会上,iOS 7.0 推出了 NSURLSession,对 Foundation URL 加载系统进行了彻底的重构,提供了更丰富的 API 来处理网络请求,如:支持 http2.0 协议、直接把数据下载到磁盘、同一 session 发送多个请求、下载是多线程异步处理和提供全局的 session 并可以统一配置等等,提高了 NSURLSession 的易用性、灵活性,更加地适合移动开发的需求。

1.1 NSURLSesscion使用步骤

  • 创建NSMutableURLRequest
  • 使用NSURLSession对象创建NSURLSessionTask
  • 执行Task

1904340-21bb298da3106816.png

1.2 创建NSURLSession

获得共享的Session

+ (NSURLSession *)sharedSession;

自定义Session

+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(id )delegate delegateQueue:(NSOperationQueue *)queue;

1.3 创建NSURLSessionTask

常见方法

- (void)suspend; // 暂停

- (void)resume; // 恢复

- (void)cancel; // 取消

@property (readonly, copy) NSError *error; // 错误

@property (readonly, copy) NSURLResponse *response; // 响应

二. NSURLSesscion的使用

1.0 get请求

- (void)getNetwork
{
    // 1.0 url
   NSURL *url = [NSURL URLWithString:@"https://apitest3.drstrong.cn/health-cure/cure/consult/count/not-handle?doctorId=35573&token=e8dc2e74694df8689089&ucDoctorId=625371"];
    
    // 2.0 创建请求体
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
    
    // 2.1 设置header
    [request setValue:@"13979af4eda34f70a305c70a0cb86c7b" forHTTPHeaderField:@"token"];
    
    // 3.0 创建回话
    NSURLSession *session = [NSURLSession sharedSession];
    
    // 4.0 创建task
   NSURLSessionTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        // 解析数据
        NSLog(@"%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
    }];
    
    // 5.0 执行Task
    [task resume];
}

1.1 postFrom请求

- (void)postFromNetWork
{
    // 1.0 确定请求路径
    NSURL *url = [NSURL URLWithString:@"https://apitest3.drstrong.cn/health-doctor/checkVersion"];
    
    // 2.0 创建可变请求对象
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
    
    // 2.1 修改请求方法,POST必须大写
    [request setHTTPMethod:@"POST"];
    
    // 2.2 设置属性, 请求超时
    request.timeoutInterval = 5;
    
    // 2.3 设置header
    [request setValue:@"13979af4eda34f70a305c70a0cb86c7b" forHTTPHeaderField:@"token"];
    // 设置请求的格式from类型, 默认可以不设置
    [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
    
    // 2.4 设置请求体
    request.HTTPBody = [@"doctorId=35573&paltform=iphone&token=e8dc2e74694df8689089&version=2.3.1" dataUsingEncoding:NSUTF8StringEncoding];
    
    
    // 3.0 创建回话对象
    NSURLSession *session = [NSURLSession sharedSession];
    
    
    // 4.0 创建Task
    NSURLSessionTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        //4.解析数据
        NSLog(@"%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
        
        NSHTTPURLResponse *res = (NSHTTPURLResponse *)response;
        NSLog(@"%zd",res.statusCode);
        NSLog(@"%@",[NSThread currentThread]);
    }];
    
    // 5.0 执行Task
    [task resume];
}

1.2 postJson请求

- (void)useSessionDelegate
{
    // 1.0 确定请求路径
    NSURL *url = [NSURL URLWithString:@"https://apitest3.drstrong.cn/health-im/im/group/infosNew?token=e8dc2e74694df8689089&doctorId=35573"];
    
    // 2.0 创建可变请求对象
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
    
    // 2.1 修改请求方法,POST必须大写
    [request setHTTPMethod:@"POST"];
    
    // 2.2 设置属性, 请求超时
    request.timeoutInterval = 5;
    
    // 2.3 设置header
    [request setValue:@"990472e1ff8146fca638597f5f0e51d2" forHTTPHeaderField:@"token"];
    // 设置请求的格式json类型, 必须要设置
    [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    
    // 2.4 设置请求体
    NSMutableDictionary *bodyDic = [NSMutableDictionary dictionary];
    bodyDic[@"identityType"] = @"1";
    bodyDic[@"empty"] = @(NO);
    bodyDic[@"doctorId"] = @"35573";
    bodyDic[@"token"] = @"e8dc2e74694df8689089";
    bodyDic[@"groupIds"] = @"171565222264834,170922752409603";
    
    
   NSData *jsonData = [NSJSONSerialization dataWithJSONObject:bodyDic options:kNilOptions error:nil];
    request.HTTPBody = jsonData;
    
    /*
     3.0 创建回话对象
     参数1: 配置信息
     参数2: 代理
     参数3: 代理在那个线程中执行
     */
    NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
    NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
   /*  NSURLSessionConfiguration: 对象用于初始化NSURLSession对象。当NSURLSession开启多个任务Task的时候可以用NSURLSessionConfiguration对象统一配置。会话在初始化时复制它们的配置,NSURLSession有一个只读的配置属性,设置该配置对这个会话无效。配置是在初始化时被读取一次,之后都是不会变化的。
    
        一. NSURLSessionConfiguration有三个类构造函数
        (1). defaultSessionConfiguration: 返回标准默认配置,一般我们都使用这个
        (2). ephemeralSessionConfiguration: 返回一个预设配置,没有持久性存储的缓存,Cookie或证书。可以用来实现像"无痕浏览"功能的功能。
        (3). backgroundSessionConfiguration:独特之处在于,它会创建一个后台会话。它甚至可以在应用程序挂起,退出,崩溃的情况下运行上传和下载任务。
    
        NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
        请求超时时间
        configuration.timeoutIntervalForRequest = 10;
        加载资源超时时间
        configuration.timeoutIntervalForResource = 10;
        蜂窝网络状态下是否可用
        configuration.allowsCellularAccess = YES;
    */
    
    
    // 4.0 创建Task
    NSURLSessionTask *task = [session dataTaskWithRequest:request];
    
    // 5.0 执行Task
    [task resume];
}


#pragma mark - <NSURLSessionDataDelegate>

/// 1.0 接收到服务器的响应 它默认会取消该请求
/// @param session 当前回话
/// @param dataTask 请求任务
/// @param response 响应头
/// @param completionHandler 回调, 传给系统
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
    NSLog(@"%@", response);
    
    NSLog(@"%s",__func__);
    /*
     NSURLSessionResponseCancel = 0,取消 默认
     NSURLSessionResponseAllow = 1, 接收
     NSURLSessionResponseBecomeDownload = 2, 变成下载任务
     NSURLSessionResponseBecomeStream        变成流
     */
    completionHandler(NSURLSessionResponseAllow);
}


/// 2.0 接收到服务器返回的数据 调用多次
/// @param session 回话对象
/// @param dataTask 请求任务
/// @param data 本次下载的数据
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
     NSLog(@"%s",__func__);
    //解析数据, 多次调用
    NSLog(@"----%@----",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
    
    
   NSDictionary *param = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
    
    NSLog(@"%@", param);
    
}

/// 3.0 请求结束或者失败时候调用
/// @param session 回话对象
/// @param task 请求任务
/// @param error 错误信息
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
                           didCompleteWithError:(nullable NSError *)error
{
    NSLog(@"%s",__func__);
}

1.3 下载请求(Block)

//优点:不需要担心内存
//缺点:无法监听文件下载进度
-(void)download
{
    //1.url
    NSURL *url = [NSURL URLWithString:@"https://file.drstrong.cn/user/6746656/feedback/006b057e47b34811bc07cff310a398ac"];
    
    //2.创建请求对象
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    
    //3.创建session
    NSURLSession *session = [NSURLSession sharedSession];
    
    //4.创建Task
    /*
     第一个参数:请求对象
     第二个参数:completionHandler 回调
        location:
        response:响应头信息
        error:错误信息
     */
    //该方法内部已经实现了边接受数据边写沙盒(tmp)的操作
    NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        
        //6.处理数据
        NSLog(@"%@---%@",location,[NSThread currentThread]);
        
        //6.1 拼接文件全路径
        NSString *fullPath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:response.suggestedFilename];
        
        //6.2 剪切文件
        [[NSFileManager defaultManager]moveItemAtURL:location toURL:[NSURL fileURLWithPath:fullPath] error:nil];
        NSLog(@"%@",fullPath);
    }];
    
    //5.执行Task
    [downloadTask resume];
}

1.4 下载请求(Delegate)

-(void)delegate
{
    //1.url
    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_03.png"];
    
    //2.创建请求对象
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    
    //3.创建session
    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    
    //4.创建Task
    NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request];
    
    //5.执行Task
    [downloadTask resume];
}

#pragma mark ----------------------
#pragma mark NSURLSessionDownloadDelegate
/**
 *  写数据
 *
 *  @param session                   会话对象
 *  @param downloadTask              下载任务
 *  @param bytesWritten              本次写入的数据大小
 *  @param totalBytesWritten         下载的数据总大小
 *  @param totalBytesExpectedToWrite  文件的总大小
 */
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
    //1. 获得文件的下载进度
    NSLog(@"%f",1.0 * totalBytesWritten/totalBytesExpectedToWrite);
}

/**
 *  当恢复下载的时候调用该方法
 *
 *  @param fileOffset         从什么地方下载
 *  @param expectedTotalBytes 文件的总大小
 */
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
{
    NSLog(@"%s",__func__);
}

/**
 *  当下载完成的时候调用
 *
 *  @param location     文件的临时存储路径
 */
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
    NSLog(@"%@",location);
    
    //1 拼接文件全路径
    NSString *fullPath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:downloadTask.response.suggestedFilename];
    
    //2 剪切文件
    [[NSFileManager defaultManager]moveItemAtURL:location toURL:[NSURL fileURLWithPath:fullPath] error:nil];
    NSLog(@"%@",fullPath);
}

/**
 *  请求结束
 */
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
    NSLog(@"didCompleteWithError");
}

1.5 下载请求(断点下载)

- (IBAction)startBtnClick:(id)sender
{
    //1.url
    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"];
    
    //2.创建请求对象
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    
    //3.创建session
    NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
    self.session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    
    //4.创建Task
    NSURLSessionDownloadTask *downloadTask = [self.session downloadTaskWithRequest:request];
    
    //5.执行Task
    [downloadTask resume];
    
    self.downloadTask = downloadTask;

}

//暂停是可以恢复
- (IBAction)suspendBtnClick:(id)sender
{
    NSLog(@"+++++++++++++++++++暂停");
    [self.downloadTask suspend];
}

//cancel:取消是不能恢复
//cancelByProducingResumeData:是可以恢复
- (IBAction)cancelBtnClick:(id)sender
{
    NSLog(@"+++++++++++++++++++取消");
    //[self.downloadTask cancel];
    
    //恢复下载的数据!=文件数据
    [self.downloadTask cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
        self.resumData = resumeData;
    }];
}

- (IBAction)goOnBtnClick:(id)sender
{
    NSLog(@"+++++++++++++++++++恢复下载");
    if(self.resumData)
    {
        self.downloadTask = [self.session downloadTaskWithResumeData:self.resumData];
    }
    
    [self.downloadTask resume];
}

代理同上
...

1.5 下载请求(断点续传下载)

  • 同NSURLConnention一个揍性, 不能用downloadTask方法
@implementation ViewController

-(void)viewDidLoad
{
    [super viewDidLoad];
    
    //1.读取保存的文件总大小的数据,0
    //2.获得当前已经下载的数据的大小
    //3.计算得到进度信息
    
}
-(NSString *)fullPath
{
    if (_fullPath == nil) {
        
        //获得文件全路径
        _fullPath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:FileName];
    }
    return _fullPath;
}

-(NSURLSession *)session
{
    if (_session == nil) {
        //3.创建会话对象,设置代理
        /*
         第一个参数:配置信息 [NSURLSessionConfiguration defaultSessionConfiguration]
         第二个参数:代理
         第三个参数:设置代理方法在哪个线程中调用
         */
        _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    }
    return _session;
}
-(NSURLSessionDataTask *)dataTask
{
    if (_dataTask == nil) {
        //1.url
        NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"];
        
        //2.创建请求对象
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
        
        //3 设置请求头信息,告诉服务器请求那一部分数据
        self.currentSize = [self getFileSize];
        NSString *range = [NSString stringWithFormat:@"bytes=%zd-",self.currentSize];
        [request setValue:range forHTTPHeaderField:@"Range"];
        
        //4.创建Task
        _dataTask = [self.session dataTaskWithRequest:request];
    }
    return _dataTask;
}

-(NSInteger)getFileSize
{
    //获得指定文件路径对应文件的数据大小
    NSDictionary *fileInfoDict = [[NSFileManager defaultManager]attributesOfItemAtPath:self.fullPath error:nil];
    NSLog(@"%@",fileInfoDict);
    NSInteger currentSize = [fileInfoDict[@"NSFileSize"] integerValue];
    
    return currentSize;
}
- (IBAction)startBtnClick:(id)sender
{
    [self.dataTask resume];
}

- (IBAction)suspendBtnClick:(id)sender
{
    NSLog(@"_________________________suspend");
    [self.dataTask suspend];
}

//注意:dataTask的取消是不可以恢复的
- (IBAction)cancelBtnClick:(id)sender
{
      NSLog(@"_________________________cancel");
    [self.dataTask cancel];
    self.dataTask = nil;
}

- (IBAction)goOnBtnClick:(id)sender
{
      NSLog(@"_________________________resume");
    [self.dataTask resume];
}

#pragma mark ----------------------
#pragma mark NSURLSessionDataDelegate
/**
 *  1.接收到服务器的响应 它默认会取消该请求
 *
 *  @param session           会话对象
 *  @param dataTask          请求任务
 *  @param response          响应头信息
 *  @param completionHandler 回调 传给系统
 */
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
    //获得文件的总大小
    //expectedContentLength 本次请求的数据大小
    self.totalSize = response.expectedContentLength + self.currentSize;
    
    if (self.currentSize == 0) {
        //创建空的文件
        [[NSFileManager defaultManager]createFileAtPath:self.fullPath contents:nil attributes:nil];
        
    }
    //创建文件句柄
    self.handle = [NSFileHandle fileHandleForWritingAtPath:self.fullPath];
    
    //移动指针
    [self.handle seekToEndOfFile];
    
    /*
     NSURLSessionResponseCancel = 0,取消 默认
     NSURLSessionResponseAllow = 1, 接收
     NSURLSessionResponseBecomeDownload = 2, 变成下载任务
     NSURLSessionResponseBecomeStream        变成流
     */
    completionHandler(NSURLSessionResponseAllow);
}

/**
 *  接收到服务器返回的数据 调用多次
 *
 *  @param session           会话对象
 *  @param dataTask          请求任务
 *  @param data              本次下载的数据
 */
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{

    // [self.handle seekToEndOfFile]; 不用写, writeData会自动移动到末尾
    // 写入数据到文件
    [self.handle writeData:data];
    
    // 计算文件的下载进度
    self.currentSize += data.length;
    NSLog(@"%f",1.0 * self.currentSize / self.totalSize);
    
    self.proessView.progress = 1.0 * self.currentSize / self.totalSize;
}

/**
 *  请求结束或者是失败的时候调用
 *
 *  @param session           会话对象
 *  @param dataTask          请求任务
 *  @param error             错误信息
 */
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
    NSLog(@"%@",self.fullPath);
    
    //关闭文件句柄
    [self.handle closeFile];
    self.handle = nil;
}

-(void)dealloc
{
    //清理工作
    //finishTasksAndInvalidate
    [self.session invalidateAndCancel];
}

@end

1.6 实现文件上传

@implementation ViewController

-(NSURLSession *)session
{
    if (_session == nil) {
        NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
        
        //是否运行蜂窝访问
        config.allowsCellularAccess = YES;
        config.timeoutIntervalForRequest = 15;
        
        _session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    }
    return _session;
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    [self upload];
}

-(void)upload
{
    //1.url
    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/upload"];
    
    //2.创建请求对象
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    
    //2.1 设置请求方法
    request.HTTPMethod = @"POST";

    //2.2 设请求头信息
    [request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@",Kboundary] forHTTPHeaderField:@"Content-Type"];
    
    //3.创建会话对象
//    NSURLSession *session = [NSURLSession sharedSession];
    
    //4.创建上传TASK
    /*
     第一个参数:请求对象
     第二个参数:传递是要上传的数据(请求体)
     第三个参数:
     */
   NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithRequest:request fromData:[self getBodyData] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
       
       //6.解析
       NSLog(@"%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
    }];
    
    //5.执行Task
    [uploadTask resume];
}

-(NSData *)getBodyData
{
    NSMutableData *fileData = [NSMutableData data];
    //5.1 文件参数
    /*
     --分隔符
     Content-Disposition: form-data; name="file"; filename="Snip20160225_341.png"
     Content-Type: image/png(MIMEType:大类型/小类型)
     空行
     文件参数
     */
    [fileData appendData:[[NSString stringWithFormat:@"--%@",Kboundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [fileData appendData:KNewLine];
    
    //name:file 服务器规定的参数
    //filename:Snip20160225_341.png 文件保存到服务器上面的名称
    //Content-Type:文件的类型
    [fileData appendData:[@"Content-Disposition: form-data; name=\"file\"; filename=\"Sss.png\"" dataUsingEncoding:NSUTF8StringEncoding]];
    [fileData appendData:KNewLine];
    [fileData appendData:[@"Content-Type: image/png" dataUsingEncoding:NSUTF8StringEncoding]];
    [fileData appendData:KNewLine];
    [fileData appendData:KNewLine];
    
    UIImage *image = [UIImage imageNamed:@"Snip20160226_90"];
    //UIImage --->NSData
    NSData *imageData = UIImagePNGRepresentation(image);
    [fileData appendData:imageData];
    [fileData appendData:KNewLine];
    
    //5.2 非文件参数
    /*
     --分隔符
     Content-Disposition: form-data; name="username"
     空行
     123456
     */
    [fileData appendData:[[NSString stringWithFormat:@"--%@",Kboundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [fileData appendData:KNewLine];
    [fileData appendData:[@"Content-Disposition: form-data; name=\"username\"" dataUsingEncoding:NSUTF8StringEncoding]];
    [fileData appendData:KNewLine];
    [fileData appendData:KNewLine];
    [fileData appendData:[@"123456" dataUsingEncoding:NSUTF8StringEncoding]];
    [fileData appendData:KNewLine];
    
    //5.3 结尾标识
    /*
     --分隔符--
     */
    [fileData appendData:[[NSString stringWithFormat:@"--%@--",Kboundary] dataUsingEncoding:NSUTF8StringEncoding]];
    return fileData;
}

#pragma mark ----------------------
#pragma mark NSURLSessionDataDelegate
/*
 *  @param bytesSent                本次发送的数据
 *  @param totalBytesSent           上传完成的数据大小
 *  @param totalBytesExpectedToSend 文件的总大小
 */
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
{
    NSLog(@"%f",1.0 *totalBytesSent / totalBytesExpectedToSend);
}
@end

二. NSURLSessionConfiguration

1.0 概述

  • NSURLSessionConfiguration对象用于初始化NSURLSession对象。

  • 展开请求级别中与NSMutableURLRequest相关的可供选择的方案,我们可以看到NSURLSessionConfiguration对于会话如何产生请求,提供了相当多的控制和灵活性。从网络访问性能,到cookie,安全性,缓存策略,自定义协议,启动事件设置,以及用于移动设备优化的几个新属性,你会发现你一直在寻找的,正是NSURLSessionConfiguration。

  • 会话在初始化时复制它们的配置,NSURLSession有一个只读的配置属性,使得该配置对象上的变化对这个会话的政策无效。配置在初始化时被读取一次,之后都是不会变化的。

1.1 构造方法

  • NSURLSessionConfiguration有三个类构造函数,这很好地说明了NSURLSession是为不同的用例而设计的。
+ "defaultSessionConfiguration"返回标准配置,这实际上与NSURLConnection的网络协议栈是一样的,具有相同的共享NSHTTPCookieStorage,共享NSURLCache和共享NSURLCredentialStorage。

+ "ephemeralSessionConfiguration"返回一个预设配置,没有持久性存储的缓存,Cookie或证书。这对于实现像"秘密浏览"功能的功能来说,是很理想的。

+ "backgroundSessionConfiguration":独特之处在于,它会创建一个后台会话。后台会话不同于常规的,普通的会话,它甚至可以在应用程序挂起,退出,崩溃的情况下运行上传和下载任务。初始化时指定的标识符,被用于向任何可能在进程外恢复后台传输的守护进程提供上下文。
  • 想要查看更多关于后台会话的信息,可以查看WWDC Session 204: “What’s New with Multitasking”

1.2 Configuration属性

NSURLSessionConfiguration拥有20个属性。熟练掌握这些属性的用处,将使应用程序充分利用其网络环境。

  • 最重要的属性:
1. 替代 request 中的 forHTTPHeaderField 告诉服务器有关客户端的附加信息
"HTTPAdditionalHeaders"指定了一组默认的可以设置出站请求的数据头。这对于跨会话共享信息,如内容类型,语言,用户代理,身份认证,是很有用的。


2. WebDav的身份验证
NSString *userPasswordString = [NSString stringWithFormat:@"%@:%@", user, password];

NSData * userPasswordData = [userPasswordString dataUsingEncoding:NSUTF8StringEncoding];

NSString *base64EncodedCredential = [userPasswordData base64EncodedStringWithOptions:0];

NSString *authString = [NSString stringWithFormat:@"Basic: %@", base64EncodedCredential];

3. 设置客户端类型
NSString *userAgentString = @"iPhone AppleWebKit";
configuration.HTTPAdditionalHeaders = @{@"Accept": @"application/json",
                                        @"Accept-Language": @"en",
                                        @"Authorization": authString,
                                        @"User-Agent": userAgentString};
                                        
4. "networkServiceType(网络服务类型)"对标准的网络流量,网络电话,语音,视频,以及由一个后台进程使用的流量进行了区分。大多数应用程序都不需要设置这个



5. "allowsCellularAccess(允许蜂窝访问)""discretionary(自行决定)"被用于节省通过蜂窝连接的带宽。建议在使用后台传输的时候,使用discretionary属性,而不是allowsCellularAccess属性,因为它会把WiFi和电源可用性考虑在内



6. "timeoutIntervalForRequest""timeoutIntervalForResource"指定了请求以及该资源的超时时间间隔。许多开发人员试图使用timeoutInterval去限制发送请求的总时间,但这误会了timeoutInterval的意思:报文之间的时间。timeoutIntervalForResource实际上提供了整体超时的特性,这应该只用于后台传输,而不是用户实际上可能想要等待的任何东西



7. "HTTPMaximumConnectionsPerHost"是 Foundation 框架中URL加载系统的一个新的配置选项。它曾经被用于NSURLConnection管理私人连接池。现在有了NSURLSession,开发者可以在需要时限制连接到特定主机的数量



8. "HTTPShouldUsePipelining"也出现在NSMutableURLRequest,它可以被用于开启HTTP管道,这可以显着降低请求的加载时间,但是由于没有被服务器广泛支持,默认是禁用的



9. "sessionSendsLaunchEvents" 是另一个新的属性,该属性指定该会话是否应该从后台启动



10. "connectionProxyDictionary"指定了会话连接中的代理服务器。同样地,大多数面向消费者的应用程序都不需要代理,所以基本上不需要配置这个属性

关于连接代理的更多信息可以在 CFProxySupport Reference 找到。
  • "Cookie Policies"
"HTTPCookieStorage" 是被会话使用的cookie存储。默认情况下,NSHTTPCookieShorage的 + sharedHTTPCookieStorage会被使用,这与NSURLConnection是相同的

-"HTTPCookieAcceptPolicy" 决定了该会话应该接受从服务器发出的cookie的条件

-"HTTPShouldSetCookies" 指定了请求是否应该使用会话HTTPCookieStorage的cookie
  • "Security Policies"
URLCredentialStorage 是会话使用的证书存储。默认情况下,NSURLCredentialStorage 的+ sharedCredentialStorage 会被使用使用,这与NSURLConnection是相同的

"TLSMaximumSupportedProtocol""TLSMinimumSupportedProtocol" 确定是否支持SSLProtocol版本的会话
  • "Caching Policies"
URLCache 是会话使用的缓存。默认情况下,NSURLCache 的 + sharedURLCache 会被使用,这与NSURLConnection是相同的

requestCachePolicy 指定了一个请求的缓存响应应该在什么时候返回。这相当于NSURLRequest 的-cachePolicy方法
  • "Custom Protocols"
protocolClasses是注册NSURLProtocol类的特定会话数组