美文网首页iOS Developer
【iOS基础】网络篇-NSURLConnection和NSURL

【iOS基础】网络篇-NSURLConnection和NSURL

作者: 亲爱的大倩倩 | 来源:发表于2017-08-15 16:56 被阅读228次

    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)非持续连接 限制每次连接只处理一个请求,服务器对客户端的请求做出响应后,马上断开连接,这种方式可以节省传输时间

    在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.设置接收数据,并且控制器要实现<NSURLSessionDataDelegate>
    @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和NSURL

        本文链接:https://www.haomeiwen.com/subject/oypmrxtx.html