前言
最近计算机网络该学习网络层的内容,想着自己把iOS中网络请求的部分好好复习一下
以及看面经时的断点续传自己一直不知道怎么实现,所以统一来了解一下
iOS开发中的网络下载方式包括NSData(最原始,实际开发基本不会用),NSURLConnection(古老又过气的苹果原生网络框架),NSURLSession(现在流行的苹果网络框架),AFNetworking,SDWebImage以及基于AFNetworking的二次封装框架例如XMNetworking,HYBNetworking等等
最原始的网络下载 – NSData + NSURL方式
- 步骤:NSString -> NSURL -> NSData -> UIImage
- 关键API
URLWithString dataWithContentsOfURL:url imageWithData:data - 示例:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURL *url = [NSURL URLWithString:@"......"];
NSData *data = [NSData dataWithContentsOfURL:url];
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = [UIImage imageWithData:data];
});
});
NSURLConnection 和 NSURLSession
- iOS9.0之后,以前使用的NSURLConnection过期,苹果推荐使用NSURLSession来替换NSURLConnection来完成网络请求相关操作
- NSURLSession的优势:
- NSURLSession支持http2.0协议
- 支持下载任务的时候可以直接把数据下载到磁盘中
- 支持后台下载和上传
- 同一个session发送多次请求,只需要建立一次连接(复用了TCP)
- 提供了全局的session并且可以统一配置,使用更加方便
- 下载的时候时多线程异步处理,效率更高
- NSURLSessionTask及其子类
NSURLSessionTask 本身是一个抽象类,在使用时,通常是根据具体的需求使用它的几个子类:NSURLSessionDataTask 可以用来发送常见的Get,Post请求,既可以用来上传也可以用来下载NSURLSessionDownloadTask 可以用来发送下载请求,专门用来下载数据NSURLSessionUploadTask 可以用来发送上传请求,专门用来上传数据
GET请求
过程如下:
- 确定请求路径(一般由公司的后台发开人员以接口文档的方式提供),GET请求参数直接跟在URL后面
- 创建请求对象(默认包含了请求头和请求方法【GET】)
- 创建会话对象(NSURLSession)
- 根据会话对象创建请求任务(NSURLSessionDataTask)
- 执行Task
- 得到服务器返回的响应后,解析数据
下载完的事件采用block形式
- 第一个API,通过request来提供参数(当然requese是基于URL的)
NSURL *url = [NSURL URLWithString:@"http:
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error == nil) {
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
NSLog(@"%@",dict);
}
}];
[task resume];
dataTaskWithURL:completionHandler:
这两个API就使用上来说无非就是第一个使用request将URL进行了封装,但是要注意直接通过URL来提供请求对象的方法在POST请求种不能使用。
下载完的事件采用delegate形式
- (void)viewDidLoad {
[super viewDidLoad];
NSURL *url = [NSURL URLWithString:@"http:
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request];
[task resume];
}
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {
NSLog(@"%@",response);
completionHandler(NSURLSessionResponseAllow);
}
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
NSLog(@"%@",data);
}
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
NSLog(@"%@",error);
}
POST请求
简单来说 POST请求需要另外单独设置request.HTTPMethod属性
NSURLSession *session = [NSURLSession sharedSession];
NSURL *url = [NSURL URLWithString:@"http:
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
request.HTTPBody = [@"phone=xxxxxxxxxxxx" dataUsingEncoding:NSUTF8StringEncoding];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (!error) {
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
NSLog(@"%@",dict);
} else {
NSLog(@"%@",error);
}
}];
[dataTask resume];
GET和POST操作的区别
区别:
- GET向服务器获取数据,POST向服务器发送请求
- GET会把查询字符串的参数追加到URL的末尾,POST请求则把数据作为请求的主体来提交,可以包含非常多的数据,因此客户可以看到GET提交的参数,POST则不可以。
- GET请求提交的数据直接加到URL的末尾,所以大小有限制,而POST则没有
- POST安全性比GET高
- 对于get方式,服务器端有Request.QueryString来获取变量的值,对于post方式,服务器用Request.Form来获取提交的数据
- get形式的URL对搜索引擎更加友好,可以提高搜索引擎排名。而POST甚至会阻止爬虫和搜索引擎的访问
使用情况
使用POST方法
- 请求的结果有持续性的影响改变,比如向数据库内添加新的数据行
- 表单收集的数据过多,若使用get方式会使URL过长
- 要传送的数据不是采用7位的ASCII编码
使用GET方法
- 请求是为了查找资源,表单的数据只是用来帮助搜索
- 请求结果无持续性的影响改变
- 收集的数据及HTML表单内的输入字段名称的总长不超过1024个字节
HTTP与HTTPS
在使用时,如果不设置Info文件,会出现某些问题:
那么HTTP与HTTPS有什么区别呢
HTTP协议运行在TCP之上,明文运输,客户端与服务器端都无法验证对方的身份;HTTPS是身披SSL(Secure Socket Layer)外壳的Http,运行于SSL上,SSL运行在TCP上,是添加了加密和认证机制的HTTP。Https的加密机制是一种共享密钥加密和公开密钥加密并用的混合加密机制。
- 端口不同:Http和Https使用不同的连接方式,http是80,https是443
- 资源消耗:和Http通信相比,Https通信会由于加减密处理消耗更多的CPU和内存资源
- 开销:Https通信需要证书,而证书一般需要向认证机构购买
NSURLSessionConfiguration
NSURLSession创建的Task任务,只能在任务结束的completionHandler的block中获取到结束后的数据,想要使用这些数据的话也需要等到下载完成了,才能拿来使用,至于下载的过程中,想要使用数据不可能。而NSURlSessionConfiguration就是一个代理,是为了监控下载过程的。
所以除了上面的两种session的创建方式sharedSession 、sessionWithConfiguration:delegate:delegateQueue: ,还有sessionWithConfiguration: 可以帮助我们监控下载过程。
创建方式
NSURLSessionConfiguration可以设置请求的Cookie、密钥、缓存、请求头等参数,将网络请求的一些配置参数从NSURLSession中分离出来。
NSURLSessionConfiguration提供defaultSessionConfiguration的方式创建,但这并不是单例方法,而是类方法,创建的是不同对象。通过这种方式创建的configuration,并不会共享cookie、cache、密钥等,而是不同configuration都需要单独设置。
文件下载
这里URL为http://vfx.mtime.cn/Video/2017/03/31/mp4/170331093811717750.mp4 一个在线的视频流
很简单,通过Session创建一个downloadTask,并调用resume即可开启一个下载任务
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:config
delegate:self
delegateQueue:[NSOperationQueue mainQueue]];
NSURL *url = [NSURL URLWithString:@"http:
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request];
[downloadTask resume];
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
}
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location {
}
当然我们也可以调用suspend 将下载任务挂起,随后调用resume 方法继续下载任务,suspend 和resume 需要是成对的。但是suspend 挂起任务是由超市的,默认是60s,如果超时系统会将TCP连接断开,我们再调用resume是失效的。可以通过NSURLSessionConfiguration 的timeoutIntervalForResource 来设置上传和下载的资源耗时。suspend 只针对于下载任务,其他任务挂起后将会重新开始。
断点续传
断点续传就是 断点续传就是从文件赏赐中断的地方重新开始下载或者上传数据,而不是从头文件开始。
HTTP协议支持断点续传操作,在开始下载请求时通过请求头设置Range 字段,标示从什么位置开始下载。
Range:bytes=512000-
服务端收到客户端请求后,开始从512kb的位置开始传输数据,并通过Content-Range 字段告知客户端传输数据的开始位置
Content-Range:bytes 512000-/1024000
downloadTask 任务开始请求后,可以调用cancelByProducingResumeData: 方法可以取消下载,并且可以获得一个resumeData ,resumeData 中存放一些断点下载的信息。可以将resumedata 写到本地,后面通过这个文件可以进行断点续传。
NSString *library = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES).firstObject;
NSString *resumePath = [library stringByAppendingPathComponent:[self.downloadURL md5String]];
[self.downloadTask cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
[resumeData writeToFile:resumePath atomically:YES];
}];
在创建下载任务前,可以判断当前任务有没有之前待恢复的任务,如果有的话调用downloadTaskWithResumeData: 方法并传入一个resumeData ,可以恢复之前的下载,并重新创建一个downloadTask 任务。
NSString *library = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES).firstObject;
NSString *resumePath = [library stringByAppendingPathComponent:[self.downloadURL md5String]];
NSData *resumeData = [[NSData alloc] initWithContentsOfFile:resumePath];
self.downloadTask = [self.session downloadTaskWithResumeData:resumeData];
[self.downloadTask resume];
通过suspend和resume这种方式挂起的任务,downloadTask是同一个对象,而通过cancel然后resumeData恢复的任务,会创建一个新的downloadTask任务。
当调用downloadTaskWithResumeData 方法恢复下载之后,会产生回调下面的方法。
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes;
fileOffset 是上次文件的下载大小 expectedTotalBytes 是预估的文件总大小
通过这两个参数,我们也能给出某些比例之类的。比如下载了多少等等
NSURLSessionTaskTransactionMetrics
NSURLSessionTaskTransactionMetrics 中的属性都是用来做统计的,功能都是记录某个值,并没有逻辑上的意义。
@property (copy, readonly) NSURLRequest *request;
@property (nullable, copy, readonly) NSURLResponse *response;
@property (nullable, copy, readonly) NSDate *fetchStartDate;
@property (nullable, copy, readonly) NSDate *domainLookupStartDate;
@property (nullable, copy, readonly) NSDate *domainLookupEndDate;
@property (nullable, copy, readonly) NSDate *connectStartDate;
@property (nullable, copy, readonly) NSDate *connectEndDate;
@property (nullable, copy, readonly) NSDate *secureConnectionStartDate;
@property (nullable, copy, readonly) NSDate *secureConnectionEndDate;
@property (nullable, copy, readonly) NSDate *requestStartDate;
@property (nullable, copy, readonly) NSDate *requestEndDate;
@property (nullable, copy, readonly) NSDate *responseStartDate;
@property (nullable, copy, readonly) NSDate *responseEndDate;
@property (nullable, copy, readonly) NSString *networkProtocolName;
@property (assign, readonly, getter=isProxyConnection) BOOL proxyConnection;
@property (assign, readonly, getter=isReusedConnection) BOOL reusedConnection;
@property (assign, readonly) NSURLSessionTaskMetricsResourceFetchType resourceFetchType;
@property (nullable, copy, readonly) NSString *localAddress;
@property (nullable, copy, readonly) NSNumber *localPort;
@property (nullable, copy, readonly) NSString *remoteAddress;
@property (nullable, copy, readonly) NSNumber *remotePort;
@property (nullable, copy, readonly) NSNumber *negotiatedTLSProtocolVersion;
@property (readonly, getter=isCellular) BOOL cellular;
这个怎么调用啊 一堆属性,一个init方法,一个new方法,怎么才能查看具体的TCp连接时间呢? 不知道 也没搜到 那说这有啥用啊…
|