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

在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);
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,717评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,501评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,311评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,417评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,500评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,538评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,557评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,310评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,759评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,065评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,233评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,909评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,548评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,172评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,420评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,103评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,098评论 2 352

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,646评论 18 139
  • 前言 简单介绍: // OSI(开放式系统互联), 由ISO(国际化标准组织)制定 // 1. 应用层 // 2....
    GitHubPorter阅读 1,237评论 2 4
  • iOS网络编程读书笔记 Facade Tester客户端门面模式的实例(被动版本化) 被动版本化,所以硬编码URL...
    melouverrr阅读 1,605评论 3 7
  • iOS开发系列--网络开发 概览 大部分应用程序都或多或少会牵扯到网络开发,例如说新浪微博、微信等,这些应用本身可...
    lichengjin阅读 3,650评论 2 7
  • JSON JSON和XML都是需要解析的 JSON是一种轻量级的数据格式,一般用于数据交互服务器返回给客户端的数据...
    JonesCxy阅读 1,852评论 2 10