文章目录
###NSURLConnection简介
-
NSURLConnection
负责发送请求,建立客户端和服务器的连接;
发送数据给服务器,并接收来自服务器的响应数据。 -
NSURLRequest
一个NSURLRequest
对象就代表一个请求,它包含的信息有一个NSURL
对象,请求方法、请求头、请求体
请求超时等。 -
NSURL
:请求地址。 -
NSURLResponse
:响应体 -
OC中几种请求数据的方式比较:0
-
第三方框架
-
注意
:NSURLConnection
在IOS9之后,已经被苹果废弃,取而代之的是iOS7之后出现NSURLSession
。
###NSURLConnection的使用步骤
- 创建一个
NSURL
对象,设置请求路径,确定要访问的资源; - 传入
NSURL
创建一个NSURLRequest
对象,设置请求头和请求体; - 使用
NSURLConnection
建立网络连接,将请求发送给服务器; - 数据处理
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
NSMutableURLRequest
是NSURLRequest
的子类,常用方法有:
设置请求超时等待时间(超过这个时间就算超时,请求失败)
- (void)setTimeoutInterval:(NSTimeInterval)seconds;
设置请求方法(比如GET和POST)
- (void)setHTTPMethod:(NSString *)method;
设置请求体
- (void)setHTTPBody:(NSData *)data;
设置请求头
- (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field;
####创建GET
和POST
请求
- 创建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);
}];
}
- 创建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]);
} ];
}
结果:
- 参考文档
HTTP响应头和请求头信息对照表
HTTP请求方法对照表
####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布局:
- 主要代码:
#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
没有提供pause
的API接口方法,只有cancel
方法,显然pause和cancel意义是不一样的,那我们该如何实现断点下载呢,我们可以从HTTP协议请求头的Range
入手。Range可以指定每次从网络下载数据包的大小。只要我们初始化的时候设置好HTTP协议请求头的Range就好了,然后在代理方法中稍微做一些逻辑处理就可以实现断点续传。- 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
-
结果如下:
-
所以实例demo原码: NSURLConnection详解原码
NSURLConnection笔记-上传文件