iOS网络篇-NSURLConnection和NSURLSession

iOS7之前,苹果使用NSURLConnection请求网络(HTTP数据传输提供的一系列接口),它在iOS9之后被弃用
iOS7后,苹果新出了NSURLSession,比NSURLConnection强大

网络编程基础

  • 本地服务器的主机地址:127.0.0.1 / localhost
  • URL

URL, 通过1个URL,能找到互联网上唯一的1个资源
URL格式: 协议:// 主机地址 / 路径
协议: 不同的协议,代表不同的资源查找方式,资源传输方式,常见协议有HTTP / file / mailto / FTP
主机地址: 存放资源的主机(服务器)的IP地址(域名)
路径: 资源在主机(服务器)中的具体位置
(1)HTTP
超文本传输协议,访问的是远程的网络资源,格式是http://
http协议是在网络开发中最常用的协议
(2)file
访问的是本地计算机上的资源,格式是file://(不用加主机地址)
(3)mailto
访问的是电子邮件地址,格式是mailto:
(4)FTP
访问的是共享主机的文件资源,格式是ftp://

注意:URL不允许写中文

NSString *urlStr=[NSString stringWithFormat:@"http://192.168.1.53:8080/Server/login?username=倩倩&pwd=111111"];
NSURL *url=[NSURL URLWithString:urlStr];
设断点可以看到url = nil

所以若有中文需要进行转码

NSString *urlStr=[NSString stringWithFormat:@"http://192.168.1.53:8080/Server/login?username=倩倩&pwd=111111"];
urlStr= [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url=[NSURL URLWithString:urlStr];

打印可以看到url = http://192.168.1.53:8080/Server/login?username=%E5%80%A9%E5%80%A9&pwd=111111

  • HTTP协议

HTTP的全称是Hypertext Transfer Protocol,超文本传输协议
1)规定客户端和服务器之间的数据传输格式
2)让客户端和服务器能有效地进行数据沟通
3)非持续连接 限制每次连接只处理一个请求,服务器对客户端的请求做出响应后,马上断开连接,这种方式可以节省传输时间

iOS网络篇-NSURLConnection和NSURLSession_第1张图片

在HTTP/1.1协议中,定义了8种发送http请求的方法
GET、POST、OPTIONS、HEAD、PUT、DELETE、TRACE、CONNECT、PATCH
PUT :增
DELETE :删
POST:改
GET:查

  • get和post请求

在IOS里面,请求行和请求头都不用写
GET和POST的主要区别表现在数据传递上

GET:
在请求URL后面以?的形式跟上发给服务器的参数,多个参数之间用&隔开,比如http://ww.test.com/login?username=123&pwd=234&type=JSON
注意:由于浏览器和服务器对URL长度有限制,因此在URL后面附带的参数是有限制的,通常不能超过1KB
相对POST请求而言,GET请求的所有参数都直接暴露在URL中,请求的URL一般会记录在服务器的访问日志中,而服务器的访问日志是黑客攻击的重点对象之一

POST:
发给服务器的参数全部放在请求体中
理论上,POST传递的数据量没有限制(具体还得看服务器的处理能力)

选择GET和POST的建议
(1)如果要传递大量数据,比如文件上传,只能用POST请求
(2)GET的安全性比POST要差些,如果包含机密\敏感信息,建议用POST
(3)如果仅仅是索取数据(数据查询),建议使用GET
(4)如果是增加、修改、删除数据,建议使用POST

在iOS中,常见的发送HTTP请求(GET和POST)的解决方案有
(1)苹果原生(自带)
NSURLConnection:用法简单,最古老最经典最直接的一种方案
NSURLSession:iOS 7新出的技术,功能比NSURLConnection更加强大
CFNetwork:NSURL*的底层,纯C语言
(2)第三方框架
ASIHttpRequest:外号“HTTP终结者”,功能极其强大,可惜早已停止更新
AFNetworking:简单易用,提供了基本够用的常用功能

NSURL:请求地址
NSURLRequest:封装一个请求,保存发给服务器的全部数据,包括一个NSURL对象,请求方法、请求头、请求体....
请求对象内部默认已经包含了请求头和请求方法(GET)
NSMutableURLRequest:NSURLRequest的子类

所以网络请求步骤是:
1.创建一个NSURL对象,设置请求路径
2.传入NSURL创建一个NSURLRequest对象,设置请求头和请求体(创建请求对象)
3.发送请求,建立客户端和服务器的连接。发送NSURLRequest的数据给服务器,并收集来自服务器的响应数据
(使用NSURLConnection/NSURLSession/AFNetworking等发送NSURLRequest)

NSMutableURLRequest是NSURLRequest的子类,常用方法有
设置请求超时等待时间(超过这个时间就算超时,请求失败)
- (void)setTimeoutInterval:(NSTimeInterval)seconds;
设置请求方法(比如GET和POST)
- (void)setHTTPMethod:(NSString *)method;
设置请求体
- (void)setHTTPBody:(NSData *)data;
设置请求头
- (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field;

创建Get请求

//    1.设置请求路径
NSString *urlStr=[NSString stringWithFormat:@"http://192.168.1.53:8080/Server/login?username=qianqian&pwd=111111"];
NSURL *url=[NSURL URLWithString:urlStr];
//    2.创建请求对象
NSURLRequest *request=[NSURLRequest requestWithURL:url];
//    3.发送请求

创建POST请求

// 1.设置请求路径,不需要传递参数
NSURL *URL=[NSURL URLWithString:@"http://192.168.1.53:8080/Server/login"];
//    2.创建请求对象,并设置请求超时为5秒
NSMutableURLRequest *request=[NSMutableURLRequest requestWithURL:URL];
request.timeoutInterval=5.0;
//设置请求体
NSString *param=[NSString stringWithFormat:@"username=qianqian&pwd=111111"];
//把拼接后的字符串转换为data,设置请求体
request.HTTPBody=[param dataUsingEncoding:NSUTF8StringEncoding];
//    3.发送请求

发送请求时注意

1.发送同步请求(一直在等待服务器返回数据,这行代码会卡住,如果服务器,没有返回数据,那么在主线程UI会卡住不能继续执行操作)有返回值
2.发送异步请求:没有返回值
3.NSURLRequest默认都是get请求

其他注意:

为了强制增强数据访问安全, iOS9默认会把 所有的http请求,所有从NSURLConnection 、 CFURL 、 NSURLSession发出的 HTTP 请求,都改为 HTTPS 请求
iOS9.x-SDK编译时,默认会让所有从NSURLConnection 、 CFURL 、 NSURLSession发出的 HTTP 请求统一采用TLS 1.2 协议。因为 AFNetworking 现在的版本底层使用了 NSURLConnection ,众多App将被影响(基于iOS8.x-SDK的App不受影响)。服务器因此需要更新,以解析相关数据。如不更新,可通过在 Info.plist 中声明,倒退回不安全的网络请求。而这一做法,官方文档称为ATS,全称为App Transport Security,是iOS9的一个新特性。
在info.plist中添加
App Transport Security Settings
Allow Arbitrary Loads


NSURLConnection

同步请求:
在主线程执行
一直在等待服务器返回数据,这行代码会卡住,如果服务器没有返回数据,那么在主线程UI会卡住不能继续执行操作

//下面代码,当执行到sendSynchronousRequest时会卡住
//因为这个url是假的,是get请求,没有设置请求超时时间,所以大概会卡住两分钟才会执行下面的NSLog语句
NSString *urlStr=[NSString stringWithFormat:@"http://192.168.1.53:8080/Server/login?username=%@&pwd=%@",@"qianqian",@"111111"];
NSURL *url=[NSURL URLWithString:urlStr];
NSURLRequest *request=[NSURLRequest requestWithURL:url];
NSData *data=[NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
NSLog(@"--%lu--",(unsigned long)data.length);

异步请求:
1)使用block回调
使用异步方法(一调用这个方法,它会自动开启一个子线程去发送请求,当请求成功,数据返回的时候自动调用内部的代码段,这个代码段在那个线程执行取决于队列,如果是主队列,那么在子线程发送请求成功拿到服务器的数据后,回到主线程中解析数据,刷新UI界面)
2)代理
需要监听网络请求的过程(如下载文件需监听文件下载进度)

block回调Demo
1.执行为发送代码,主线程继续往下走
当拿到服务器的返回数据的数据的时候再回调block,执行block代码段。这种情况不会卡住主线程。
2.队列的作用:决定这个block操作放在哪个线程执行
3.使用NSJSONSerialization 返回的对象, 如果是{}那就是字典,[]那就是数组等

NSString *urlStr=[NSString stringWithFormat:@"http://192.168.1.53:8080/Server/login?username=%@&pwd=%@",@"qianqian",@"111111"];
NSURL *url=[NSURL URLWithString:urlStr];
NSURLRequest *request=[NSURLRequest requestWithURL:url];
//获取一个主队列,发送异步请求
NSOperationQueue *queue=[NSOperationQueue mainQueue];
//创建一个队列(默认添加到该队列中的任务异步执行),发送异步请求
//NSOperationQueue *queue=[[NSOperationQueue alloc]init];
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError)
{
    NSLog(@"--block回调数据--%@---%lu", [NSThread currentThread],(unsigned long)data.length);
    if (data)//data不能为空,为空会崩溃
    {
        NSDictionary *dict=[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
        NSLog(@"--dic是--%@",dict);
    }
}];
NSLog(@"请求发送完毕");

代理Demo
数据的处理
在didReceiveData:方法中,拼接接收到的所有数据,等所有数据都拿到后,在connectionDidFinishLoading:方法中进行处理

NSString *urlStr=[NSString stringWithFormat:@"http://192.168.1.53:8080/Server/login?username=%@&pwd=%@",@"qianqian",@"111111"];
NSURL *url=[NSURL URLWithString:urlStr];
NSMutableURLRequest *request=[NSMutableURLRequest  requestWithURL:url];
request.timeoutInterval=5.0;
//使用代理发送异步请求(通常应用于文件下载)
NSURLConnection *conn=[NSURLConnection connectionWithRequest:request delegate:self];
[conn start];
NSLog(@"已经发出请求AAAAAAAA---");

#pragma mark- NSURLConnectionDataDelegate代理方法
//当接收到服务器的响应(连通了服务器)时会调用
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    NSLog(@"111接收到服务器的响应%@",response);
}

//当接收到服务器的数据时会调用(可能会被调用多次,每次只传递部分数据)
 -(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    NSLog(@"222接收到服务器的数据%@",data);
}

//当服务器的数据加载完毕时就会调用
 -(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    NSLog(@"服务器的数据加载完毕");
}

//请求错误(失败)的时候调用(请求超时\断网\没有网\,一般指客户端错误)
 -(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    NSLog(@"请求错误%@",error);
}

NSURLSession

GET请求

发送GET请求Demo1:(不会卡住主线程)
1.确定请求路径
2.创建请求对象
3.创建会话对象(NSURLSession
4.根据会话对象创建请求任务(NSURLSessionDataTask)
5.执行Task
6.当得到服务器返回的响应后,解析数据(XML|JSON|HTTP)

    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=qianqian&pwd=520it&type=JSON"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    NSURLSession *session = [NSURLSession sharedSession];
    NSURLSessionDataTask *dataTask = [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);
                                          }
                                          
                                      }];
    [dataTask resume];

发送GET请求Demo2:(不会卡住主线程)
此方法只能发送GET请求,不可发送POST请求
通过URL初始化task,在block内部可以直接对返回的数据进行处理

    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=qianqian&pwd=520it&type=JSON"];
    NSURLSession *session = [NSURLSession sharedSession];
    NSURLSessionDataTask *dataTask = [session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error)
                                      {
                                           NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
                                          NSLog(@"%@",dict);
                                      }];
    [dataTask resume];
POST请求

POST请求区别是
1.创建可变的请求对象
2.修改请求方法为POST
3.设置请求体,把参数转换为二进制数据并设置请求体

    NSURLSession *session = [NSURLSession sharedSession];
    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login"];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    request.HTTPMethod = @"POST";
    request.HTTPBody = [@"username=520it&pwd=520it&type=JSON" dataUsingEncoding:NSUTF8StringEncoding];
    NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error)
                                      {
                                          NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
                                           NSLog(@"%@",dict);
                                      }];
    [dataTask resume];

NSURLSession代理
有的时候需要监听网络请求的过程(如下载文件需监听文件下载进度),那么就需要用到代理方法

###1.发送请求
    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=520it&pwd=520it&type=JSON"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    //获得会话对象,并设置代理
    /*
     第一个参数:会话对象的配置信息defaultSessionConfiguration 表示默认配置
     第二个参数:谁成为代理,此处为控制器本身即self
     第三个参数:队列,该队列决定代理方法在哪个线程中调用,可以传主队列|非主队列
     [NSOperationQueue mainQueue]   主队列:   代理方法在主线程中调用
     [[NSOperationQueue alloc]init] 非主队列: 代理方法在子线程中调用
     */
    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc]init]];
    
    //根据会话对象创建一个Task(发送请求)
    NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request];
    //执行任务
    [dataTask resume];

###2.设置接收数据,并且控制器要实现
@property (nonatomic, strong) NSMutableData *responseData;
-(NSMutableData *)responseData
{
    if (_responseData == nil)
    {
        _responseData = [NSMutableData data];
    }
    return _responseData;
}

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

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

//3.当请求完成(成功|失败)的时候会调用该方法,如果请求失败,则error有值
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
    NSLog(@"didCompleteWithError--%@",[NSThread currentThread]);
    
    if(error == nil)
    {
        //解析数据
        NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:self.responseData options:kNilOptions error:nil];
        NSLog(@"收到的数据为%@",dict);
    }
}

你可能感兴趣的:(iOS网络篇-NSURLConnection和NSURLSession)