当前位置:首页 » 《随便一记》 » 正文

[iOS开发]网络请求合集_复杂化的博客

7 人参与  2021年12月30日 15:58  分类 : 《随便一记》  评论

点击全文阅读


文章目录

  • 前言
  • 最原始的网络下载 -- NSData + NSURL方式
  • NSURLConnection 和 NSURLSession
    • GET请求
      • 下载完的事件采用block形式
      • 下载完的事件采用delegate形式
    • POST请求
    • GET和POST操作的区别
      • 使用情况
        • 使用POST方法
        • 使用GET方法
    • HTTP与HTTPS
  • NSURLSessionConfiguration
    • 创建方式
    • 文件下载
    • 断点续传
  • NSURLSessionTaskTransactionMetrics

前言

最近计算机网络该学习网络层的内容,想着自己把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的dataWithContentsOfURL:方法下载
        NSData *data = [NSData dataWithContentsOfURL:url];
        // 回到主线程,刷新UI
        dispatch_async(dispatch_get_main_queue(), ^{
            self.imageView.image = [UIImage imageWithData:data];
        });
    });

NSURLConnection 和 NSURLSession

  1. iOS9.0之后,以前使用的NSURLConnection过期,苹果推荐使用NSURLSession来替换NSURLConnection来完成网络请求相关操作
  2. NSURLSession的优势:
  • NSURLSession支持http2.0协议
  • 支持下载任务的时候可以直接把数据下载到磁盘中
  • 支持后台下载和上传
  • 同一个session发送多次请求,只需要建立一次连接(复用了TCP)
  • 提供了全局的session并且可以统一配置,使用更加方便
  • 下载的时候时多线程异步处理,效率更高
  1. NSURLSessionTask及其子类
  • NSURLSessionTask本身是一个抽象类,在使用时,通常是根据具体的需求使用它的几个子类:
  • NSURLSessionDataTask可以用来发送常见的Get,Post请求,既可以用来上传也可以用来下载
  • NSURLSessionDownloadTask可以用来发送下载请求,专门用来下载数据
  • NSURLSessionUploadTask可以用来发送上传请求,专门用来上传数据
    在这里插入图片描述

GET请求

过程如下:

  1. 确定请求路径(一般由公司的后台发开人员以接口文档的方式提供),GET请求参数直接跟在URL后面
  2. 创建请求对象(默认包含了请求头和请求方法【GET】)
  3. 创建会话对象(NSURLSession)
  4. 根据会话对象创建请求任务(NSURLSessionDataTask)
  5. 执行Task
  6. 得到服务器返回的响应后,解析数据

下载完的事件采用block形式

  • 第一个API,通过request来提供参数(当然requese是基于URL的)
    //1. 确定请求路径
    NSURL *url = [NSURL URLWithString:@"http://news-at.zhihu.com/api/4/news/latest"];
    //2. 创建请求对象
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    //3. 创建会话对象
    NSURLSession *session = [NSURLSession sharedSession];
    //4. 根据会话对象创建请求任务
    NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        if (error == nil) {
            //6.解析服务器返回的数据
            //说明:(此处返回的数据是JSON格式的,因此使用NSJSONSerialization进行反序列化处理)
            NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
            
            NSLog(@"%@",dict);
            
        }
    }];
    [task resume];
  • 第二个API,直接通过URL来提供参数
dataTaskWithURL:completionHandler:

这两个API就使用上来说无非就是第一个使用request将URL进行了封装,但是要注意直接通过URL来提供请求对象的方法在POST请求种不能使用。

下载完的事件采用delegate形式

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    //1. 确定请求路径
    NSURL *url = [NSURL URLWithString:@"http://news-at.zhihu.com/api/4/news/latest"];
    //2. 创建请求对象
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    //3. 创建会话对象
    //    第一个参数:会话对象的配置信息defaultSessionConfiguration  表示默认配置
//   第二个参数:谁成为代理,此处为控制器本身即self
//   第三个参数:队列,该队列决定代理方法在哪个线程中调用
//   可以传主队列、非主队列 如果不指定线程,则completionHandler和delegate的回调方法,都会在子线程中执行。
    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    //4. 根据会话对象创建请求任务
    NSURLSessionDataTask *task = [session dataTaskWithRequest:request];
    [task resume];
}

//1.接收到服务器响应的时候调用该方法
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {
    NSLog(@"%@",response);
    completionHandler(NSURLSessionResponseAllow);
    //注意:需要使用completionHandler回调告诉系统应该如何处理服务器返回的数据
    //默认是取消的
    /*
     NSURLSessionResponseCancel = 0,        默认的处理方式,取消
     NSURLSessionResponseAllow = 1,         接收服务器返回的数据
     NSURLSessionResponseBecomeDownload = 2,变成一个下载请求
     NSURLSessionResponseBecomeStream        变成一个流
     */
}

//2.接收到服务器返回数据的时候会调用该方法,如果数据较大那么该方法可能会调用多次
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
    NSLog(@"%@",data);
}

//3.当请求完成(成功|失败)的时候会调用该方法,如果请求失败,则error有值
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
    NSLog(@"%@",error);
}

POST请求

简单来说 POST请求需要另外单独设置request.HTTPMethod属性

    //1.创建会话对象
    NSURLSession *session = [NSURLSession sharedSession];
    
    //2.根据会话对象创建task
    NSURL *url = [NSURL URLWithString:@"http://116.62.21.180:8088/user/get_detail"];
    
    //3.创建可变的请求对象
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    
    //4.修改请求方法为POST
    request.HTTPMethod = @"POST";
    
    //5.设置请求体
    request.HTTPBody = [@"phone=xxxxxxxxxxxx" dataUsingEncoding:NSUTF8StringEncoding];
    
    //6.根据会话对象创建一个Task(发送请求)
    /*
     第一个参数:请求对象
     第二个参数:completionHandler回调(请求完成【成功|失败】的回调)
     data:响应体信息(期望的数据)
     response:响应头信息,主要是对服务器端的描述
     error:错误信息,如果请求失败,则error有值
     */
    NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        if (!error) {
            //8.解析数据
            NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
            NSLog(@"%@",dict);
        } else {
            NSLog(@"%@",error);
        }
    }];
    
    //7.执行任务
    [dataTask resume];

在这里插入图片描述

GET和POST操作的区别

区别:

  1. GET向服务器获取数据,POST向服务器发送请求
  2. GET会把查询字符串的参数追加到URL的末尾,POST请求则把数据作为请求的主体来提交,可以包含非常多的数据,因此客户可以看到GET提交的参数,POST则不可以。
  3. GET请求提交的数据直接加到URL的末尾,所以大小有限制,而POST则没有
  4. POST安全性比GET高
  5. 对于get方式,服务器端有Request.QueryString来获取变量的值,对于post方式,服务器用Request.Form来获取提交的数据
  6. get形式的URL对搜索引擎更加友好,可以提高搜索引擎排名。而POST甚至会阻止爬虫和搜索引擎的访问

使用情况

使用POST方法

  1. 请求的结果有持续性的影响改变,比如向数据库内添加新的数据行
  2. 表单收集的数据过多,若使用get方式会使URL过长
  3. 要传送的数据不是采用7位的ASCII编码

使用GET方法

  1. 请求是为了查找资源,表单的数据只是用来帮助搜索
  2. 请求结果无持续性的影响改变
  3. 收集的数据及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的创建方式sharedSessionsessionWithConfiguration: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://vfx.mtime.cn/Video/2017/03/31/mp4/170331093811717750.mp4"];
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方法继续下载任务,suspendresume需要是成对的。但是suspend挂起任务是由超市的,默认是60s,如果超时系统会将TCP连接断开,我们再调用resume是失效的。可以通过NSURLSessionConfigurationtimeoutIntervalForResource来设置上传和下载的资源耗时。suspend只针对于下载任务,其他任务挂起后将会重新开始。

断点续传

断点续传就是 断点续传就是从文件赏赐中断的地方重新开始下载或者上传数据,而不是从头文件开始。
在这里插入图片描述

HTTP协议支持断点续传操作,在开始下载请求时通过请求头设置Range字段,标示从什么位置开始下载。

Range:bytes=512000-

服务端收到客户端请求后,开始从512kb的位置开始传输数据,并通过Content-Range字段告知客户端传输数据的开始位置

Content-Range:bytes 512000-/1024000

downloadTask任务开始请求后,可以调用cancelByProducingResumeData:方法可以取消下载,并且可以获得一个resumeDataresumeData中存放一些断点下载的信息。可以将resumedata写到本地,后面通过这个文件可以进行断点续传。

NSString *library = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES).firstObject;
NSString *resumePath = [library stringByAppendingPathComponent:[self.downloadURL md5String]];//以URL为值,创建一个path
[self.downloadTask cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
    [resumeData writeToFile:resumePath atomically:YES];//将得到的resumeData传入url的路径中
}];

在创建下载任务前,可以判断当前任务有没有之前待恢复的任务,如果有的话调用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;
// 响应对象,请求失败可能会为nil
@property (nullable, copy, readonly) NSURLResponse *response;
// 请求开始时间
@property (nullable, copy, readonly) NSDate *fetchStartDate;
// DNS解析开始时间
@property (nullable, copy, readonly) NSDate *domainLookupStartDate;
// DNS解析结束时间,如果解析失败可能为nil
@property (nullable, copy, readonly) NSDate *domainLookupEndDate;
// 开始建立TCP连接时间
@property (nullable, copy, readonly) NSDate *connectStartDate;
// 结束建立TCP连接时间
@property (nullable, copy, readonly) NSDate *connectEndDate;
// 开始TLS握手时间
@property (nullable, copy, readonly) NSDate *secureConnectionStartDate;
// 结束TLS握手时间
@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;
// 网络协议,例如http/1.1
@property (nullable, copy, readonly) NSString *networkProtocolName;
// 请求是否使用代理
@property (assign, readonly, getter=isProxyConnection) BOOL proxyConnection;
// 是否复用已有连接
@property (assign, readonly, getter=isReusedConnection) BOOL reusedConnection;
// 资源标识符,表示请求是从Cache、Push、Network哪种类型加载的
@property (assign, readonly) NSURLSessionTaskMetricsResourceFetchType resourceFetchType;
// 本地IP
@property (nullable, copy, readonly) NSString *localAddress;
// 本地端口号
@property (nullable, copy, readonly) NSNumber *localPort;
// 远端IP
@property (nullable, copy, readonly) NSString *remoteAddress;
// 远端端口号
@property (nullable, copy, readonly) NSNumber *remotePort;
// TLS协议版本,如果是http则是0x0000
@property (nullable, copy, readonly) NSNumber *negotiatedTLSProtocolVersion;
// 是否使用蜂窝数据
@property (readonly, getter=isCellular) BOOL cellular;

这个怎么调用啊 一堆属性,一个init方法,一个new方法,怎么才能查看具体的TCp连接时间呢?
不知道 也没搜到 那说这有啥用啊…


点击全文阅读


本文链接:http://zhangshiyu.com/post/32415.html

请求  数据  下载  
<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1