iOS网络编程
一、使用NSURLConnection发送请求
NSURLConnection常见的发送请求方法有以下几种
- 同步请求
+ (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error;
- 异步请求:根据对服务器返回数据的处理方式不同又可以分为两种
- block回调
+ (void)sendAsynchronousRequest:(NSURLRequest*) request queue:(NSOperationQueue*) queue completionHandler:(void (^)(NSURLResponse* response, NSData* data, NSError* connectionError)) handler
-
- 代理:NSURLConnectionDataDelegate,在这种情况下需要调用start方法开始发送请求
/** * 请求错误(失败)的时候调用(请求超时、断网、没有网络,一般指客户端错误) */ - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error; /** * 当接收到服务器的响应(连通了服务器)就会调用 */ - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response; /** * 当接收到服务器的数据就会调用(可能会被调用多次,每次调用只会传递部分数据) */ - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data; /** * 当服务器的数据接收完毕后就会调用 */ - (void)connectionDidFinishLoading:(NSURLConnection *)connection;
1. 发送GET请求
1 - (void)viewDidLoad { 2 [super viewDidLoad]; 3 4 NSString *username = @"zhangsan"; 5 NSString *pwd = @"12345"; 6 7 // 发送请求GET给服务器 8 // GET请求:请求行\请求头 9 10 // 1.设置请求路径 11 NSString *urlStr = [NSString stringWithFormat:@"http://localhost:8080/MJServer/login?username=%@&pwd=%@", username, pwd]; 12 NSURL *url = [NSURL URLWithString:[urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; 13 14 // 2.创建请求对象(默认就是GET请求) 15 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; 16 17 // 3.发送请求(同步) 18 // [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil]; 19 20 // 在队列中执行请求(可同步也可异步) 21 [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { 22 // 服务器处理请求后返回的数据在这里进行解析,即解析data 23 }]; 24 }
2. 发送POST请求
1 - (void)viewDidLoad { 2 [super viewDidLoad]; 3 4 NSString *username = @"zhangsan"; 5 NSString *pwd = @"12345"; 6 7 // 发送请求POST给服务器 8 // POST请求:请求体 9 10 // 1.设置请求路径 11 NSURL *url = [NSURL URLWithString:@"http://localhost:8080/MJServer/login"]; 12 13 // 2.创建请求对象(默认就是GET请求) 14 NSURLRequest *request = [NSURLRequest requestWithURL:url]; 15 // 设置请求超时 16 request.timeoutInterval = 5; 17 // 设置为POST请求 18 request.HTTPMethod = @"POST"; 19 // 设置请求体 20 NSString *param = [NSString stringWithFormat:@"username=%@&pwd=%@", username, pwd]; 21 request.HTTPBody = [param dataUsingEncoding:NSUTF8StringEncoding]; 22 23 // 通过请求头告诉服务器客户端的类型 24 [request setValue:@"iOS" forHTTPHeaderField:@"User-Agent"]; 25 26 27 // 3.发送请求(同步) 28 // 在队列中执行请求(可同步也可异步) 29 [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { 30 // 服务器处理请求后返回的数据在这里进行解析,即解析data 31 }]; 32 }
二、大文件下载
小文件下载
- 直接用NSData的+ (instancetype)dataWithContentsOfURL:(NSURL *)url;
- 利用NSURLConnection发送一个HTTP请求去下载
- 如果是下载图片,可以利用SDWebImage框架
1. 简单下载
@property (nonatomic, strong) NSFileHandle *writeHandle; - (void)viewDidLoad { [super viewDidLoad]; // 创建并发送请求 NSURL *url = [NSURL URLWithString:@"http://localhost:8080/MJServer/resources/video.zip"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; [NSURLConnection connectionWithRequest:request delegate:self]; } #pragma mark - NSURLConnectionDataDelegate 代理方法 /** * 当接收到服务器的响应(连通了服务器)就会调用 */ - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { // 1.文件的存储路径 NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; NSString *filePath = [caches stringByAppendingPathComponent:@"video.zip"]; // 2.创建一个空的文件到沙盒中 NSFileManager *manager = [NSFileManager defaultManager]; // 刚创建完毕的大小是0字节 [manager createFileAtPath:filePath contents:nil attributes:nil]; // 3.创建写数据的文件句柄 self.writeHandle = [NSFileHandle fileHandleForWritingAtPath:filePath]; } /** * 当接收到服务器的数据就会调用(可能会被调用多次,每次调用只会传递部分数据) */ - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { // 移动到文件的尾部 [self.writeHandle seekToEndOfFile]; // 从当前移动的位置(文件尾部)开始写入数据 [self.writeHandle writeData:data]; } /** * 当服务器的数据接收完毕后就会调用 */ - (void)connectionDidFinishLoading:(NSURLConnection *)connection { // 关闭连接(不再输入数据到文件中) [self.writeHandle closeFile]; }
2. 暂停、恢复下载
思路:当暂停下载是请求就已经结束了,要恢复下载需要重新发送请求。可以在发送请求时设置请求头告诉服务器从哪里开始下载。
1 // 只下载前100个字节 2 [request setValue:@"bytes=0-99" forKey:@"Range"];
Range示例
bytes=0-99 从0到100的头100个字节
bytes=500-999 从500到999的第二个500租户额
bytes=500- 从500字节以后的所有字节
bytes=-500 最后的500字节
bytes=500-599,800-899 同时指定几个范围
三、NSURLCache缓存
一般只对GET请求进行缓存,不必对POST请求进行缓存。
NSURLCache的常见用法
- 获得全局缓存对象(没必要手动创建)
NSURLCache *cache = [NSURLCache sharedURLCache];
- 设置内存缓存的最大容量(单位是字节,默认为512KB)
@property NSUInteger memoryCapacity;
- 设置硬盘缓存的最大容量(单位是字节,默认为10MB),硬盘缓存位置:沙盒/Library/Caches
@property NSUInteger diskCapacity;
只要设置了缓存策略,系统就会自动利用NSURLCache进行数据缓存
1 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; 2 // 设置缓存策略 3 request.cachePolicy = NSURLRequestReturnCacheDataElseLoad;
iOS对NSURLRequest提供了7中缓存策略(实际上只有4中可用)
// 默认的缓存策略(取决于协议) NSURLRequestUseProtocolCachePolicy = 0, // 忽略缓存,重新请求 NSURLRequestReloadIgnoringLocalCacheData = 1, //未实现(不可用) NSURLRequestReloadIgnoringLocalAndRemoteCacheData = 4, // 忽略缓存,重新请求 NSURLRequestReloadIgnoringCacheData = NSURLRequestReloadIgnoringLocalCacheData, // 有缓存就用缓存,没缓存就重新请求 NSURLRequestReturnCacheDataElseLoad = 2, // 有缓存就用缓存,没有缓存就不发送请求,当做请求出错处理(离线模式) NSURLRequestReturnCacheDataDontLoad = 3, //未实现(不可用) NSURLRequestReloadRevalidatingCacheData = 5,
缓存的相关操作
1 NSURLCache *cache = [NSURLCache sharedURLCache]; 2 // 取得某个请求的缓存 3 NSCachedURLResponse *response = [cache cachedResponseForRequest:request]; 4 if (response) { 5 NSLog(@"这个请求已经存在缓存"); 6 }else { 7 NSLog(@"这个请求没有缓存"); 8 } 9 // 清除某个请求的缓存 10 [cache removeCachedResponseForRequest:request]; 11 // 清除所有缓存 12 [cache removeAllCachedResponses];
四、检测网络状态
苹果官方提供了一个叫Reachability的示例程序,便于开发者检测网络状态
https://developer.apple.com/library/ios/samplecode/Reachability/Reachability.zip
将Reachability.h和Reachability.m拖放到项目中,并导入SystemConfiguration.framework即可使用Reachability。
1 // 检测wifi状态 2 Reachability *wifi = [Reachability reachabilityForLocalWiFi]; 3 if ([wifi currentReachabilityStatus] != NotReachable) { 4 NSLog(@"有wifi"); 5 }else { 6 NSLog(@"无wifi"); 7 } 8 9 // 检测手机自带网络功能(能否上网) 10 Reachability *connection = [Reachability reachabilityForInternetConnection]; 11 if ([connection currentReachabilityStatus] != NotReachable) { 12 NSLog(@"有网络"); 13 }else { 14 NSLog(@"无网络"); 15 }
较老版本的Reachability是使用非ARC编写的,需要在项目的Build Phases的Complie Sources修改Reachability.m文件的Complie Flags为-fno--objc-arc
五、使用AFNetworking发送请求
github地址:https://github.com/AFNetworking/AFNetworking
导入主头文件: #import "AFNetworking.h"
要使用AFN发送请求,需要先创建一个请求管理者
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
然后只需调用block即可发送HTTP请求
1 /** 2 * 发送GET请求 3 * @param GET: 请求地址 4 * @param parameters: 请求参数 5 */ 6 [manager GET:@"" parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) { 7 // 请求成功时的回调 8 } failure:^(AFHTTPRequestOperation *operation, NSError *error) { 9 // 请求失败时的回调 10 }]; 11 12 /** 13 * 发送POST请求 14 * @param POST: 请求地址 15 * @param parameters: 请求参数 16 */ 17 [manager POST:@"" parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) { 18 // 请求成功时的回调 19 } failure:^(AFHTTPRequestOperation *operation, NSError *error) { 20 // 请求失败时的回调 21 }];
使用AFNetworking发送请求实例
下面以获取新浪OAuth授权的接口文档为例子。用AFN向新浪服务器发送请求
发送POST请求代码:
1 // 创建请求管理类 2 AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager]; 3 4 // 设置请求参数 5 NSMutableDictionary *params = [NSMutableDictionary dictionary]; 6 params[@"client_id"] = @"2740450720"; 7 params[@"client_secret"] = @"717606369d8f7dcda59d78f4b25e8e30"; 8 params[@"grant_type"] = @"authorization_code"; 9 params[@"code"] = code; 10 params[@"redirect_uri"] = @"http://www.baidu.com"; 11 12 // 发送请求 13 [manager POST:@"https://api.weibo.com/oauth2/access_token" parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) { 14 // 请求成功 15 NSString *accessToken = responseObject[@"access_token"]; 16 NSLog(@"请求成功:%@ - %@", responseObject ,accessToken); 17 } failure:^(AFHTTPRequestOperation *operation, NSError *error) { 18 // 请求失败 19 NSLog(@"请求失败:%@", error); 20 }]; 21
发送GET请求和发送POST请求的操作步骤相同,只是调用的方法不同而已。同样将get请求的参数用一个字典装起来作为请求参数即可。