iOS网络请求—NSURLConnection

NSURLConnection简介

  • NSURLConnection
    负责发送请求,建立客户端和服务器的连接;
    发送数据给服务器,并接收来自服务器的响应数据。

  • NSURLRequest
    一个NSURLRequest对象就代表一个请求,它包含的信息有一个NSURL对象,请求方法、请求头、请求体
    请求超时等。

  • NSURL:请求地址。

  • NSURLResponse:响应体

  • OC中几种请求数据的方式比较:0
    这里写图片描述

  • 第三方框架
    这里写图片描述
  • 注意NSURLConnection 在IOS9之后,已经被苹果废弃,取而代之的是iOS7之后出现NSURLSession

NSURLConnection的使用步骤

  1. 创建一个NSURL对象,设置请求路径,确定要访问的资源;
  2. 传入NSURL创建一个NSURLRequest对象,设置请求头和请求体;
  3. 使用NSURLConnection建立网络连接,将请求发送给服务器;
  4. 数据处理 response,data,error处理
    这里写图片描述

NSURLConnection API

1.NSURLConnection常见的发送请求方法有以下几种:
  • 同步请求
+ (nullable NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse * _Nullable * _Nullable)response error:(NSError **)error ;//ios(2.0,9.0)
  • 异步请求:根据对服务器返回数据的处理方式的不同,又可以分为2种:

(1)block回调:

+ (void)sendAsynchronousRequest:(NSURLRequest*) request queue:(NSOperationQueue*) queue completionHandler:(void (^)(NSURLResponse* _Nullable response, NSData* _Nullable data, NSError* _Nullable connectionError)) handler;//ios(5.0,9.0)

(2)代理

- (nullable instancetype)initWithRequest:(NSURLRequest *)request delegate:(nullable id)delegate startImmediately:(BOOL)startImmediately;//ios(2.0,9.0),
- (nullable instancetype)initWithRequest:(NSURLRequest *)request delegate:(nullable id)delegate;//ios(2.0,9.0),
+ (nullable NSURLConnection*)connectionWithRequest:(NSURLRequest *)request delegate:(nullable id)delegate ;//ios(2.0,9.0),
2.属性和方法:
@property (readonly, copy) NSURLRequest *originalRequest ;//ios(5.0)以上,原始请求
@property (readonly, copy) NSURLRequest *currentRequest;//ios(5.0)以上,当前请求
- (void)start;//ios(2.0),只有在在startImmediately = NO的情况下,需要调用start方法开始发送请求;
- (void)cancel;//取消请求
- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSRunLoopMode)mode;//ios(2.0),将连接实例回调加入到一个runloop,NSURLConnectionDelegate回调会在这个runloop中响应,注意该方法不能跟setDelegateQueue同时设置,只能选择一个。
- (void)unscheduleFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSRunLoopMode)mode;//ios(2.0),将取消在指定runloop中的运行,实际上就会停止NSURLConnection的运行
- (void)setDelegateQueue:(nullable NSOperationQueue*) queue;//ios(5.0),如果设置了队列,回调将会在这个队列上进行,回调一次就类似与生成了一个NSBlockOperation加入到了队列中,注意该方法不能跟scheduleInRunLoop同时设置,只能选择一个。
+ (BOOL)canHandleRequest:(NSURLRequest *)request;//判断是否能处理某个请求
3.NSURLConnectionDelegate
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;//请求失败会调用的代理方法
- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection;//否应使用凭证存储来验证连接
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;//告诉代理,将发送认证查询请求
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace;//ios(3.0,8.0),连接到一些有安全限制的网站时调用,例如:服务器信任、客户端证书、HTTP表单验证等。但URLConnection不知道也没有强制程序员必需处理哪些安全问题,因此它把一个NSURLProtectionSpace对象作为参数传递,如果程序员想响应某一类安全问题,那么在这个方法最后就返回YES。你要明白程序员可以处理哪些安全问题,你可以查看NSURLProtectionSpace的authenticationMethod属性。
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;//ios(2.0,8.0),验证服务端的证书时,调用该代理方法。
- (void)connection:(NSURLConnection *)connection didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;//ios(2.0,8.0),取消验证服务端的证书时,调用该代理方法
4.NSURLConnectionDataDelegate
- (nullable NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(nullable NSURLResponse *)response;//请求将要被发送出去之前会调用,返回值是一个NSURLRequest,就是那个真正将要被发送的请求
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;//当接收到服务器的响应(连通了服务器)时会调用
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;//当接收到服务器的数据时会调用(可能会被调用多次,每次只传递部分数据)
- (nullable NSInputStream *)connection:(NSURLConnection *)connection needNewBodyStream:(NSURLRequest *)request;
- (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite;
- (nullable NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse;//将要给返回信息缓存处理前的回调
- (void)connectionDidFinishLoading:(NSURLConnection *)connection;//服务器返回的数据完全接收完毕后调用
5.NSURLConnectionDownloadDelegate
@optional
//发送给代理以传递进度信息
/*
 参数:
 connection:哪个连接对象
 bytesWritten:当前下载的字节数
 totalBytesWritten:已经写入的字节数
 expectedTotalBytes:文件的总字节数
 注意:如果同时调用该代理方法和-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;代理方法,后者不会再被调用
 */
- (void)connection:(NSURLConnection *)connection didWriteData:(long long)bytesWritten totalBytesWritten:(long long)totalBytesWritten expectedTotalBytes:(long long) expectedTotalBytes;
//当URL连接恢复下载早先暂停的URL资源时,调用该代理(即断点续传时候调用)
/*
  参数:
 totalBytesWritten:已经写入的字节数
 expectedTotalBytes:文件的总字节数
 */
- (void)connectionDidResumeDownloading:(NSURLConnection *)connection totalBytesWritten:(long long)totalBytesWritten expectedTotalBytes:(long long) expectedTotalBytes;//当URL连接恢复早先暂停的URL资源下载时调用
@required
//下载完成时调用
- (void)connectionDidFinishDownloading:(NSURLConnection *)connection destinationURL:(NSURL *) destinationURL;

NSMutableURLRequest

NSMutableURLRequestNSURLRequest的子类,常用方法有:
设置请求超时等待时间(超过这个时间就算超时,请求失败)

- (void)setTimeoutInterval:(NSTimeInterval)seconds;

设置请求方法(比如GET和POST)

- (void)setHTTPMethod:(NSString *)method;

设置请求体

- (void)setHTTPBody:(NSData *)data;

设置请求头

- (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field;

创建GETPOST请求

  • 创建GET请求:

(1)get方式同步请求

-(void)syncGetRequest{//get同步请求
    /*
     GET:http://120.25.226.186:32812/login?username=123&pwd=456&type=JSON
     协议+主机地址+接口名称+?+参数1&参数2&参数3
     post:http://120.25.226.186:32812/login
     协议+主机地址+接口名称
     */
    //GET,没有请求体
    NSURL * url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=520it&pwd=520it&type=JSON"];
    NSURLRequest * request = [NSURLRequest requestWithURL:url];
    //请求头不需要设置(默认的请求头)
    //请求方法--->默认为GET
    //声明响应体
    NSHTTPURLResponse * response = nil;
    NSData * data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
    //json就是符合某种结构的字符串,所以可以直接转成字符串形式
    NSLog(@"strData:%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
    //JSONX序列话,将NSData类型转换成其他NSArray,NSDictionary,NSString,NSNumber等类型
    NSLog(@"data : %@",[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil]);
    NSLog(@"response:%@---code:%ld",response.allHeaderFields,response.statusCode);
}

结果:
这里写图片描述
(2)get方式异步请求

-(void)asyncGetRequest{//get异步请求
    NSURL * url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=520it"];
    NSURLRequest * request = [NSURLRequest requestWithURL:url];
    NSHTTPURLResponse * response = nil;
    [NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc]init] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
        NSLog(@"strData:%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
        NSLog(@"data : %@",[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil]);
        NSLog(@"response:%@",response);
        NSLog(@"%@",connectionError);

    }];
}

get异步请求

  • 创建POST请求
- (void)syncPostResquest{//post同步请求数据
    NSURL * url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?"];
    //创建一个可变的请求对象
    NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url];
    //修改请求方法,POST必须都大写
    request.HTTPMethod = @"POST";
    //设置请求超时时间
    request.timeoutInterval = 60.0f;
    //设置请求头User-Agent
    [request setValue:@"ios 88" forHTTPHeaderField:@"User-Agent"];
    //设置请求体,及请求参数,字符串转化为Data,所以POST请求是看不到请求参数的比较安全
    request.HTTPBody = [@"username=520it&pwd=123&type=JSON" dataUsingEncoding:NSUTF8StringEncoding];
    //请求数据
    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue currentQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
        NSLog(@"data:%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
    } ];
}

结果:
post请求

delegate方法请求

- (void)requestWithDelegate{//get请求
    NSURL * url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=520it&pwd=520it&type=JSON"];
    NSURLRequest * request = nil;
    //设置代理发送请求
    //需要检查startImmediately的值,startImmediately == YES 会马上发送请求 | startImmediately == NO 则需要调用start方法发送请求
    NSURLConnection * connection = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:YES];
//    [connection start];
}
#pragma mark NSURLConnectionDelegate
//当请求失败的时候调用
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
     NSLog(@"0.%s",__func__);
}
#pragma mark NSURLConnectionDataDelegate
//当接收到服务器响应的时候调用
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
     NSLog(@"1.%s",__func__);
}
//接收到服务器返回数据的时候调用,调用多次
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)manyData{
    NSLog(@"2.%s",__func__);
    NSLog(@"manydata:%@",[[NSString alloc]initWithData:manyData encoding:NSUTF8StringEncoding]);
    [self.resultData appendData:manyData];
    NSLog(@"resultData:%@",[[NSString alloc]initWithData:self.resultData encoding:NSUTF8StringEncoding]);
}
//请求结束的时候调用
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
    NSLog(@"3.%s",__func__);
}

结果:
代理方法请求数据

NSURLConnection下载数据并显示下载进度

  • xib布局:
    xib布局
  • 主要代码:
#import "ViewController.h"
#import <MediaPlayer/MediaPlayer.h>
@interface ViewController ()<NSURLConnectionDataDelegate>{
    NSInteger totalSize;
}
@property(nonatomic ,strong) NSMutableData * data;
@property (weak, nonatomic) IBOutlet UIProgressView *progressV;
@property (weak, nonatomic) IBOutlet UILabel *progressL;
@end

@implementation ViewController
-(NSMutableData *)data{
    if (!_data) {
        _data = [NSMutableData data];
    }
    return _data;
}

- (void)viewDidLoad {
    [super viewDidLoad];

}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    NSURL * url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"];
    NSURLRequest * request = [NSURLRequest requestWithURL:url];
    self.progressL.text = @"0%";
    self.progressV.progress = 0;
    [[NSURLConnection alloc]initWithRequest:request delegate:self];
}

#pragma mark NSURLConnectionDataDelegate
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
    //获取文件的总大小(本次请求的文件数据的总大小)
    totalSize = response.expectedContentLength;
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
    [self.data appendData:data];//累加获取数据
    //下载进度显示
    NSLog(@"进度:%f",1.0 * self.data.length/totalSize);
    self.progressV.progress = 1.0 * self.data.length/totalSize;
//    NSNumber * num = [NSNumber numberWithFloat:self.progressV.progress * 100];
//    self.progressL.text = [NSString stringWithFormat:@"%zd%%",num.integerValue];
     self.progressL.text = [NSString stringWithFormat:@"%.2f%%",self.progressV.progress * 100];
}

-(void)connectionDidFinishLoading:(NSURLConnection *)connection{    
    //创建沙盒路径
    NSString * path =  [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]stringByAppendingPathComponent:@"aaa.mp4"];
    //通过沙盒路径将数据写到沙盒中
    [self.data writeToFile:path atomically:YES];

    NSURL * url =[NSURL fileURLWithPath:path];
    MPMoviePlayerViewController * movieVC = [[MPMoviePlayerViewController alloc]initWithContentURL:url];
    [self presentMoviePlayerViewControllerAnimated:movieVC];
}

结果展示:
NSURLConnection下载数据并显示下载进度

NSURLConnection实现断点续传

  • NSURLConnection没有提供pause的API接口方法,只有cancel方法,显然pause和cancel意义是不一样的,那我们该如何实现断点下载呢,我们可以从HTTP协议请求头的Range入手。Range可以指定每次从网络下载数据包的大小。只要我们初始化的时候设置好HTTP协议请求头的Range就好了,然后在代理方法中稍微做一些逻辑处理就可以实现断点续传。
  • xib布局如下:
    xib布局
  • 主要代码:
#import "ViewController.h"
#import <MediaPlayer/MediaPlayer.h>
@interface ViewController ()<NSURLConnectionDataDelegate>{
    NSURLConnection * connection;
    NSInteger totalSize;
}
@property(nonatomic ,strong) NSMutableData * data;
@property (weak, nonatomic) IBOutlet UIProgressView *progressV;
@property (weak, nonatomic) IBOutlet UILabel *progressL;
@end

@implementation ViewController
-(NSMutableData *)data{
    if (!_data) {
        _data = [NSMutableData data];
    }
    return _data;
}
//点击下载暂停按钮
- (IBAction)downLoadOrPauseBtnClick:(UIButton *)sender {
    sender.selected = !sender.selected;
    if (sender.selected) {//点击了下载
        NSURL * url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"];
        NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url];
        NSString *range = [NSString stringWithFormat:@"bytes=%lld-", (long long)self.data.length];
        //设置请求头
        [request setValue:range forHTTPHeaderField:@"Range"];
        self.progressL.text = @"0%";
        self.progressV.progress = 0;
        connection =  [[NSURLConnection alloc]initWithRequest:request delegate:self];
    }else{//点击了暂停
        [connection cancel];//取消下载
        connection = nil;
    }
}
#pragma mark NSURLConnectionDataDelegate
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
    //获取文件的总大小(本次请求的文件数据的总大小)
    totalSize = response.expectedContentLength;
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
    [self.data appendData:data];//累加获取数据
    //下载进度显示
    NSLog(@"进度:%f",1.0 * self.data.length/totalSize);
    self.progressV.progress = 1.0 * self.data.length/totalSize;
//    NSNumber * num = [NSNumber numberWithFloat:self.progressV.progress * 100];
//    self.progressL.text = [NSString stringWithFormat:@"%zd%%",num.integerValue];
     self.progressL.text = [NSString stringWithFormat:@"%.2f%%",self.progressV.progress * 100];
}

-(void)connectionDidFinishLoading:(NSURLConnection *)connection{    
    //创建沙盒路径
    NSString * path =  [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]stringByAppendingPathComponent:@"aaa.mp4"];
    //通过沙盒路径将数据写到沙盒中
    [self.data writeToFile:path atomically:YES];
    NSURL * url =[NSURL fileURLWithPath:path];
    MPMoviePlayerViewController * movieVC = [[MPMoviePlayerViewController alloc]initWithContentURL:url];
    [self presentMoviePlayerViewControllerAnimated:movieVC];
    totalSize = 0;
    self.data = nil;
}
@end

猜你喜欢

转载自blog.csdn.net/bolted_snail/article/details/79876055