HTTP和ASI框架的使用

Xcode的插件安装路径: 
/Users/用户名/Library/Application Support/Developer/Shared/Xcode/Plug-ins

访问网络的基本流程:5点

构造NSURL实例(地址)--》生成 NSURLRequest请求 --》
通过 NSURLConnection发送请求--》通过 NSURLResponse实例和NSError返回结果 --》 接受返回数据

HTTP协议

1.面试题: 聊一下HTTP协议(协议的完整的通信过程)

*HTTP协议的全称:超文本传输协议,定制传输数据的规范(客户端和服务器之间的数据传输规范)
*描述HTTP协议完整的通信过程

2.通信过程

1> 请求
* 客户端 --> 服务器
* 请求的内容
a. "请求行":(请求方法\请求资源路径\HTTP协议版本)
b. "请求头":(描述客户端的信息,如手机的系统版本、语言等)
c. "请求体":(POST请求才需要有, 存放具体数据,如用户名账号、密码等)

2> 响应
* 服务器 --> 客户端
* 响应的内容
a. "状态行"(HTTP协议版本\状态码\状态信息\"响应行")
 *HTTP/1.1 200 OK

b. "响应头"(服务器信息, 返回数据的类型, 返回数据的长度)
Server: Apache-Coyote/1.1
Content-Type: application/jsion;charset=UTF-8
Content-Lenngth: 248

c. "实体内容"( 返回给客户端的具体内容,"响应体")
  *比如服务器返回的JSON对象
  *比如服务器返回的文件数据

--------------请求的方法-----get 和 post ----------------
3.HTTP请求的方法

1> GET
* 参数都拼接在URL后面
* 参数有限制

2> POST
* 参数都在请求体
* 参数没有限制
* 文件上传自能用POST
// -------------请求的手段------asyn 和 sync----------------

4.iOS中发送GET\POST请求的"手段3种"

1、> NSURLConnection(苹果原生态),使用起来比ASI\AFN复杂
2、> ASI (基于CFNetwork),提供了非常强大的功能,使用简单
3、> AFN (基于NSURLConnection),提供了常用功能,使用简单

"建议:"

  • 为了提高开发效率和减少调试时间,尽量使用著名的第三方框架
  • 因此使用HTTP请求,更建议使用ASI或AFN

1>" NSURLConnection"

// 1.发送一个同步请求
+ (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error;

// 2.发送一个异步请求(block)

+ (void)sendAsynchronousRequest:(NSURLRequest*) request
queue:(NSOperationQueue*) queue
completionHandler:(void (^)(NSURLResponse* response, NSData* data, NSError* connectionError)) handler;

// 3.代理的方法(异步需要实现该方法)

[NSURLConnection connectionWithRequest:request delegate:self];
[[NSURLConnection alloc] initWithRequest:request delegate:self];
[[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];

NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
[conn start];

2>文件下载(大文件下载)

    1*"实现方案":边下载边写入(写入沙河里)
    2*"具体实现步骤":
        a.在接收到服务器的"响应时"
        *.创建一个空的文件到沙盒中--NSFileManager
        *.创建写数据的文件句柄对象--NSFileHandle

        b.在接收到服务器的"数据时"
        // 利用句柄对象将服务器返回的数据写到文件的末尾
        // 移动文件的尾部
        [self.writeHandle seekToEndOfFile];
        // 从当前移动的位置(文件尾部)开始写入数据
        [self.writeHandle writeData:data];

        c.在接收完毕服务器返回的数据时
        // 关闭句柄
        [self.writeHandle closeFile];
        self.writeHandle = nil;

    3*"断点下载"
        1> 关键技术点
        *设置"请求头Range",告诉服务器下载哪一段数据(开始、结束),其它的和文件下载没区别

    4*文件上传
        1>明确
            *只能用POST请求
            *请求参数都在请求体(文件参数、非文件参数类型的普通参数)
        2>实现步骤
            a.拼接请求体(文件参数、非文件参数类型的普通参数)
            *文件参数
            //参数的开始标记(分隔线)
            --heima\r\n
            //参数描述(参数名。。。)
            Content-Disposition: form-data; name="参数名"; filename="文件名"\r\n
            //文件类型
            Content-Type: 文件的类型MIMEType\r\n
            // 文件的二进制数据(参数值)
            \r\n
            // 文件的二进制数据
            \r\n

            *非文件参数(普通参数)
            //参数的开始标记(分隔线)
            --heima\r\n
            //参数描述(参数名。。。)
            Content-Disposition: form-data; name="参数名"\r\n
            // 参数值
            \r\n
            //参数值
            \r\n

            // * 所有参数结束的标记(分隔线)
            --heima--\r\n

            b.设置请求头
                *请求体的长度
                Content-Lenngth : 请求体的长度

                *请求数据的类型
                Content-Type:
                // 普通POSTQQ:application/x-www-from-urlencoded
                // 上传文件的POST请求:multipart/form-data; boundary=--heima

3> "文件上传"

二、> ASI
1、缓存

1> 缓存单个请求

// 1、 获得全局的缓存对象(决定着缓存存储路径)
ASIDownloadCache *cache = [ASIDownloadCache sharedCache];
// 设置系统默认的缓存加载策略
cache.defaultCachePolicy = ASIOnlyLoadIfNotCachedCachePolicy;

// 2、设置请求对象的缓存对象(使用哪个缓存对象)

request.downloadCache = cache;

// 3、设置请求对象的缓存---加载策略

request.cachePolicy = ASIOnlyLoadIfNotCachedCachePolicy;

// 4、设置请求对象的缓存---存储策略(存储的时长)

request.cacheStoragePolicy = ASICachePermanentlyCacheStoragePolicy; //Permanently(永久的)

但是默认是ASICacheForSessionDurationCacheStoragePolicy
------------------------------------------------
// ----注意:缓存加载策略的优先级:request.cachePolicy > cache.defaultCachePolicy

2> 缓存所有请求
1.获得全局的缓存对象(决定缓存的存储路径,存储到什么位置)

 ASIDownloadCache *cache = [ASIDownloadCache sharedCache];
// 设置默认的缓存加载策略(默认的缓存加载策略 : 不读取缓存)
cache.defaultCachePolicy = ASIOnlyLoadIfNotCachedCachePolicy;

// 2、设置全局缓存对象

 [ASIHTTPRequest setDefaultCache:cache];

// 判断数据是否从缓存读取: BOOL useCache[request didUseCachedResponse];
// 或者设置7天,但这个要用ASI框架: [request setSecondsToCache:60 * 60 * 24 * 7];

三. > "NSURLCache"
缓存的使用步骤

// 获得全局(默认是全局)的缓存对象

NSURLCache *cache = [NSURLCache sharedURLCache];

//设置缓存容量

cache.memoryCapacity = 4 * 1024 * 1024;
cache.diskCapacity = 20 * 1024 * 1024;

//设置缓存策略(有缓存就用缓存,没有缓存就重新请求)

request.cachePolicy = NSURLRequestReturnCacheDataElseLoad;

"使用框架---发送请求":

1> "同步请求"
    [request startSynchronous];
2> "异步请求"
    [request startAsynchronous];

3> "GET\POST"

    1.GET请求
// ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];

    2.POST请求
// ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
    // 添加普通参数(非文件参数)
[request setPostValue:@"zhangsan" forKey:@"username"];
[request setPostValue:@"124" forKey:@"pwd"];

4> "文件下载"

// 文件的存储路径(文件下载到什么地方)
request.downloadDestinationPath = filepath;
// 3.设置下载代理--进度
request.downloadProgressDelegate = self.circleView; //圆形条
// request.downloadProgressDelegate = self.progressView;// 进度条
// 4.支持断点下载
request.allowResumeForFileDownloads = YES;

5> "文件上传"

//  添加文件参数(file:需要上传文件的路径)
[request setFile:file forKey:@"file"];
[request setFile:file withFileName:@"123.txt" andContentType:@"text/plain" forKey:@"file"];

[request setData:data withFileName:@"minion.png" andContentType:@"image/png" forKey:@"file"]; // (二进制,直接上传到服务器,适用于相机)

// 设置上传代理(监听上传进度)
request.uploadProgressDelegate = self.circleView;

6>------------------------ "监听请求的过程" --3种方式(代理、SEL、block)

1> 代理方法(1)

// 设置代理
request.delegate = self;
// 遵守协议
ASIHTTPRequestDelegate

// 实现代理的方法
- (void)requestStarted:(ASIHTTPRequest *)request;
- (void)request:(ASIHTTPRequest *)request didReceiveData:(NSData *)data;
- (void)requestFinished:(ASIHTTPRequest *)request;
- (void)requestFailed:(ASIHTTPRequest *)request;

2> SEL(方法2,---该方式较少用)

// 设置代理
request.delegate = self;
[request setDidStartSelector(start)]; // 开始发送请求,就会调用代理的start的方法
// 实现方法
- (void)start
{
// ....
}

3> block(方法3)

[request setStartedBlock:^{
    NSLog(@"setStartedBlock");
}];
[request setDataReceivedBlock:^{
     NSLog(@"setDataReceivedBlock");
}];
[request setCompletionBlock:^{
    NSLog(@"setCompletionBloc");
}];
[request setFailedBlock:^{
    NSLog(@"setFailedBlock---");
}];

7> 通过request对象获得服务器返回的数据(响应)

  1> 获得响应头信息
@property (atomic, retain) NSDictionary *responseHeaders;

  2> 获得响应体(实体内容)
- (NSData *)responseData; // 直接返回服务器的二进制数据
- (NSString *)responseString; // 将二进制的数据转成字符串(方便调试)

二、"框架AFN --- "

1、GET\POST

1."GET请求"
// 1.获得请求管理者

 AFHTTPRequestOperationManager *mgr = [AFHTTPRequestOperationManager manager];

// 2.封装请求参数

NSMutableDictionary *params = [NSMutableDictionary dictionary];
params[@"username"] = @"123";
params[@"pwd"] = @"123";

// 3.发送GET请求

mgr GET:@"http://192.168.1.200:8080/MJServer/login" parameters:params
success:^(AFHTTPRequestOperation *operation, id responseObject) { //  responseObject : 在这种情况下是字典
    NSLog(@"请求成功---%@", responseObject);
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"请求失败---%@", error);
}];

------------------
2."POST请求"

// 1.获得请求管理者
AFHTTPRequestOperationManager *mgr = [AFHTTPRequestOperationManager manager];
// 2.封装请求参数
NSMutableDictionary *params = [NSMutableDictionary dictionary];
params[@"username"] = @"123";
params[@"pwd"] = @"123";
// 3.发送POST请求
mgr POST:@"http://192.168.1.200:8080/MJServer/login" parameters:params
success:^(AFHTTPRequestOperation *operation, id responseObject) { //  responseObject : 在这种情况下是字典
    NSLog(@"请求成功---%@", responseObject);
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"请求失败---%@", error);
}];

2."文件上传"

// 1.获得请求管理者
AFHTTPRequestOperationManager *mgr = [AFHTTPRequestOperationManager manager];

// 2.发送请求(做文件上传)
// parameters : 只能放非文件参数
NSMutableDictionary *params = [NSMutableDictionary dictionary];
params[@"username"] = @"zhangsan";

[mgr POST:@"http://192.168.1.200:8080/MJServer/upload" parameters:params
constructingBodyWithBlock:^(id formData) {
    // 一定要在这个block中添加文件参数
    
    // 加载文件数据
    NSString *file = [[NSBundle mainBundle] pathForResource:@"test.txt" ofType:nil];
    NSData *data = [NSData dataWithContentsOfFile:file];
    
    // 拼接文件参数
    [formData appendPartWithFileData:data name:@"file" fileName:@"123.txt" mimeType:@"text/plain"];
}
  success:^(AFHTTPRequestOperation *operation, id responseObject) {
      NSLog(@"上传成功----%@", responseObject);
  } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
      NSLog(@"上传失败----%@", error);
  }];
  1. "使用框架--网络状态监控"
 1 >"使用框架Reachability"

// 使用通知--监听网络状态改变的通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkStateChange) name:kReachabilityChangedNotification object:nil];
// 创建Reachability
self.conn = [Reachability reachabilityForInternetConnection];
// 开始监控网络(一旦网络状态发生改变,就会发出kReachabilityChangedNotification)

[self.conn startNotifier];

// 处理网络状态改变
- (void)checkNetworkState
{
    // 1.检测wifi状态
    Reachability *wifi = [Reachability reachabilityForLocalWiFi];
    
    // 2.检测手机是否能上网络(WIFI\3G\2.5G)
    Reachability *conn = [Reachability reachabilityForInternetConnection];
    
    // 3.判断网络状态
    if ([wifi currentReachabilityStatus] != NotReachable) { // 有wifi
        NSLog(@"有wifi");
        
    } else if ([conn currentReachabilityStatus] != NotReachable) { // 没有使用wifi, 使用手机自带网络进行上网
        NSLog(@"使用手机自带网络进行上网");
        
    } else { // 没有网络
        
        NSLog(@"没有网络");
    }

2 >"使用框架AFN"

// 1.获得网络监控的管理者
AFNetworkReachabilityManager *mgr = [AFNetworkReachabilityManager sharedManager];// 单例

// 2.设置网络状态改变后的处理
[mgr setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
    // 当网络状态改变了, 就会调用这个block
    switch (status) {
        case AFNetworkReachabilityStatusUnknown: // 未知网络
            NSLog(@"未知网络");
            break;
            
        case AFNetworkReachabilityStatusNotReachable: // 没有网络(断网)
            NSLog(@"没有网络(断网)");
            break;
            
        case AFNetworkReachabilityStatusReachableViaWWAN: // 手机自带网络
            NSLog(@"手机自带网络");
            break;
            
        case AFNetworkReachabilityStatusReachableViaWiFi: // WIFI
            NSLog(@"WIFI");
            break;
    }
}];

// 3.开始监控

[mgr startMonitoring];

// 主动判断当前的网络是否可以用(方法)
//    mgr.isReachableViaWiFi
//    mgr.isReachableViaWWAN
-----------------------面试:--------------------------

面试:
ASI和AFN的区别?
// 回答角度:
1.性能
2.发送请求
3.处理服务器数据
4.ASI的特色
5.AFN的特色

1.性能
*ASI基于底层CFNetwork框架
*AFN基于NSURLConnection
*运行性能:ASI > AFN

  1. 处理服务器数据
    1> AFN : 根据服务器返回的数据,进行自动解析(智能)
    *服务器返回的是JSON数据,自动转换为NSDictionary 或者NSArray
    *服务器返回的是XML数据,自动转换为NSXMLParser
*

2> ASI : 并没有对服务器的数据进行解析,直接返回NSData二进制数据

3.处理请求的过程
1> AFN : success 和 failure 两个block
2> ASI : 有3种方式处理请求过程(代理方法、SEL、block)

4.ASI的特色(重点)

    1> 缓存
    2> 下载和上传
    *轻松监听请求进度
    *轻松实现断点下载(ASI没有断点上传功能,断点上传功能得使用socket技术)
    3> 提供了很多扩展接口(比如做数据压缩)
    *
 "ASIDataCompressor.h"
 "ASIDataDecompressor.h"
    *
    4> ASIHTTPRequest继承自NSOperation
    *能用队列统一管理所有请求
    *请求之间能依赖
    5> ASINetworkQueue
    *统一管理所有请求
    *如5个下载或上传请求 --> ASINetworkQueue : 监听5个请求的总进度
    *监听所有请求的开始\失败\完毕
    *shouldCancelAllRequestsOnFailure
    *YES : 队列中某个请求失败了,其它所有请求都消失
    *NO :队列中的某个请求失败了,其它请求不受影响,继续请求

5.AFN的特色(重点)
1、使用简单
2、方便

扩展:重构

//--------// 封装方法 --重构---------
- (void)viewDidLoad
    {
        // 封装前的效果 --  代码庸于
        [super viewDidLoad];
//     设置导航栏按钮
//        UIButton *leftButton = [[UIButton alloc] init];
//        [leftButton setBackgroundImage:[UIImage imageWithName:@"navigationbar_friendsearch"] forState:UIControlStateNormal];
//        [leftButton setBackgroundImage:[UIImage imageWithName:@"navigationbar_friendsearch_highlighted"] forState:UIControlStateHighlighted];
//        // 设置按钮的尺寸为背景图片的尺寸
//        leftButton.size = leftButton.currentBackgroundImage.size;
//        // 监听按钮点击
//        [leftButton addTarget:self action:@selector(friendSearch) forControlEvents:UIControlEventTouchUpInside];
//        self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:leftButton];
//        
//        // 设置右边按钮
//        UIButton *rightButton = [[UIButton alloc] init];
//        [rightButton setBackgroundImage:[UIImage imageWithName:@"navigationbar_pop"] forState:UIControlStateNormal];
//        [rightButton setBackgroundImage:[UIImage imageWithName:@"navigationbar_pop_highlighted"] forState:UIControlStateHighlighted];
//        // 设置按钮的尺寸为背景图片的尺寸
//        rightButton.size = rightButton.currentBackgroundImage.size;
//        // 监听按钮点击
//        [rightButton addTarget:self action:@selector(pop) forControlEvents:UIControlEventTouchUpInside];
//        self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:rightButton];
        
        // 封装后的效果,// 注意:target:(id)target
        // 设置导航栏按钮
        self.navigationItem.leftBarButtonItem = [UIBarButtonItem itemWithImageName:@"navigationbar_friendsearch" highImageName:@"navigationbar_friendsearch_highlighted" target:self action:@selector(friendSearch)];
        
        self.navigationItem.rightBarButtonItem = [UIBarButtonItem itemWithImageName:@"navigationbar_pop" highImageName:@"navigationbar_pop_highlighted" target:self action:@selector(pop)];
    }
    // 封装方法 // 注意:target:(id)target
    - (UIBarButtonItem *)itemWithImageName:(NSString *)imageName highImageName:(NSString *)highImageName target:(id)target action:(SEL)action
    {
        UIButton *button = [[UIButton alloc] init];
        [button setBackgroundImage:[UIImage imageWithName:imageName] forState:UIControlStateNormal];
        [button setBackgroundImage:[UIImage imageWithName:highImageName] forState:UIControlStateHighlighted];
        // 设置按钮的尺寸为背景图片的尺寸
        button.size = button.currentBackgroundImage.size;
        // 监听按钮点击
        [button addTarget:self action:@selector(pop) forControlEvents:UIControlEventTouchUpInside];
        return [[UIBarButtonItem alloc] initWithCustomView:button];
    }

// -----------封装方法----------

  - (void)viewDidLoad
  {
    [super viewDidLoad];
    // 封装前的效果
    //  UIViewController *home = [[UIViewController alloc] init];
    //  home.tabBarItem.title = @"首页";
    //   home.tabBarItem.image = [UIImage imageWithName:@"tabbar_home"];
   //...
   //...
   // 封装后的效果
   // 添加子控制器
   HMHomeViewController *home = [[HMHomeViewController alloc] init];
   [self addOneChlildVc:home title:@"首页" imageName:@"tabbar_home" selectedImageName:@"tabbar_home_selected"];        
   HMMessageViewController *message = [[HMMessageViewController alloc] init];
   [self addOneChlildVc:message title:@"消息" imageName:@"tabbar_message_center" selectedImageName:@"tabbar_message_center_selected"];        
   HMDiscoverViewController *discover = [[HMDiscoverViewController alloc] init];
   [self addOneChlildVc:discover title:@"发现" imageName:@"tabbar_discover" selectedImageName:@"tabbar_discover_selected"];        
   HMProfileViewController *profile = [[HMProfileViewController alloc] init];
   [self addOneChlildVc:profile title:@"我" imageName:@"tabbar_profile" selectedImageName:@"tabbar_profile_selected"];
}
 *  添加一个子控制器
 *  @param childVc           子控制器对象
 *  @param title             标题
  *  @param imageName         图标
  *  @param selectedImageName 选中的图标
 - (void)addOneChlildVc:(UIViewController *)childVc title:(NSString *)title imageName:(NSString *)imageName selectedImageName:(NSString *)selectedImageName
    {
        childVc.view.backgroundColor = HMRandomColor;
        // 设置标题
        // 相当于同时设置了tabBarItem.title和navigationItem.title
        childVc.title = title;
        //    childVc.tabBarItem.title = title; // tabbar标签上
        //    childVc.navigationItem.title  = title; // 导航栏
        
        // 设置图标
        childVc.tabBarItem.image = [UIImage imageWithName:imageName];
        
        // 设置选中的图标
        UIImage *selectedImage = [UIImage imageWithName:selectedImageName];
        if (iOS7) {
            // 声明这张图片用原图(别渲染)
            selectedImage = [selectedImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
        }
        childVc.tabBarItem.selectedImage = selectedImage;
      
        // 添加为tabbar控制器的子控制器
        HMNavigationController *nav = [[HMNavigationController alloc] initWithRootViewController:childVc];
        [self addChildViewController:nav];
  }

你可能感兴趣的:(HTTP和ASI框架的使用)