目录

iOS 网络小结

一.HTTP协议

1.什么是HTTP协议

HTTP协议的全称: 超文本传输协议, 定制传输数据的规范(客户端和服务器之间的数据传输规范)。

2. HTTP 通信过程

1> 请求

客户端 –> 服务器

请求的内容:

“请求行” : 请求方法\请求资源路径\HTTP协议版本

1
GET /Server/login?username=123&pwd=123&method=get&type=JSON HTTP/1.1

“请求头” : 客户端的信息

1
2
3
4
5
Host: 192.168.1.200:8080
User-Agent: iPhone Simulator; iPhone OS 9.1; en_US
Accept: text/html
Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate

“请求体” : POST请求才需要有, 存放具体数据

  1. 比如文件数据
2. 比如POST请求的参数数据

2> 响应

服务器 –> 客户端

响应的内容:

“状态行” : HTTP协议版本\状态码\状态信息, 也可称为"响应行”

1
HTTP/1.1 200 OK

“响应头” : 服务器信息\返回数据的类型\返回数据的长度

1
2
3
Server: Apache-Coyote/1.1
Content-Type: application/json;charset=UTF-8
Content-Length: 248

“实体内容” : 返回给客户端的具体内容, 也可称为"响应体”

  1. 比如服务器返回的JSON数据

  2. 比如服务器返回的文件数据

3.HTTP请求的方法

1> GET

  1. 参数都拼接在URL后面

  2. 参数有限制

2> POST

  1. 参数都在请求体

  2. 参数没有限制

  3. 文件上传只能用POST

3> HEAD : 获得响应头信息, 不获取响应体

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

1> NSURLConnection

  1. 苹果原生

  2. 使用起来, 比ASI\AFN复杂

2> ASI

  1. 基于CFNetwork

  2. 提供了非常多强大的功能, 使用简单

3> AFN

  1. 基于NSURLConnection

  2. 提供了常用的功能, 使用简单

4> 建议

  1. 为了提高开发效率和减少调试花费的时间, 尽量使用著名的简单的第三方框架

  2. 因此, 处理HTTP请求, 更建议使用ASI或者AFN

二.NSURLConnection

1.发送请求

1> 发送一个同步请求

1
+ (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error;

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

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

3> 发送一个异步请求(代理方法)

1
2
3
4
5
6
[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
3
4
5
// 创建一个空文件 - NSFileManager
[mgr createFileAtPath:self.destPath contents:nil attributes:nil];
 
// 创建一个跟空文件相关联的句柄对象 - NSFileHandle
self.writeHandle = [NSFileHandle fileHandleForWritingAtPath:self.destPath];

在接收到服务器的数据时

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

在接收完服务器返回的数据时

1
2
3
// 关闭句柄
[self.writeHandle closeFile];
self.writeHandle = nil;

3.断点下载

关键技术点:设置请求头Range, 告诉服务器下载哪一段数据

4.文件上传

1> 明确

  1. 只能用POST请求
2. 请求参数都在请求体(文件参数\非文件类型的普通参数)

2> 实现步骤

拼接请求体(文件参数\非文件类型的普通参数)

文件参数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// 参数的开始标记(分割线)
--kannng\r\n
// 参数描述(参数名...)
Content-Disposition: form-data; name="参数名"; filename="文件名"\r\n
// 文件类型
Content-Type: 文件的类型MIMEType\r\n
// 文件的二进制数据(参数值)
\r\n
文件的二进制数据
\r\n

非文件参数(普通参数)

1
2
3
4
5
6
7
8
// 参数的开始标记(分割线)
--kannng\r\n
// 参数描述(参数名...)
Content-Disposition: form-data; name="参数名"\r\n
// 参数值
\r\n
参数值
\r\n

所有参数结束的标记

1
--heima--\r\n

设置请求头

请求体的长度

1
Content-Length : 请求体的长度(字节长度)

请求数据的类型

1
2
3
Content-Type :
// 普通POST请求: application/x-www-form-urlencoded
// 上传文件的POST请求 : multipart/form-data; boundary=--heima

三.NSURLCache

1,缓存的实现

一般只对GET请求进行缓存,不必对POST请求进行缓存。

GET请求一般用来查询数据。

POST请求一般是发大量数据给服务器处理(变动性比较大)。

在iOS中,可以使用NSURLCache类缓存数据。

iOS 5之前:只支持内存缓存。

iOS 5开始:同时支持内存缓存和硬盘缓存。

缓存原理:一个 NSURLRequest对 应一个 NSCachedURLResponse。

缓存技术:数据库。

2,NSURLCache的常见方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// 获得全局缓存对象(没必要手动创建)
NSURLCache*cache = [NSURLCache sharedURLCache];
//设置内存缓存的最大容量(字节为单位,默认为512KB)
- (void)setMemoryCapacity:(NSUInteger)memoryCapacity;
//设置硬盘缓存的最大容量(字节为单位,默认为10M)
- (void)setDiskCapacity:(NSUInteger)diskCapacity;
// 硬盘缓存的位置:沙盒/Library/Caches
 
// 取得某个请求的缓存
- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest*)request;
// 清除某个请求的缓存
- (void)removeCachedResponseForRequest:(NSURLRequest*)request;
// 清除所有的缓存
- (void)removeAllCachedResponses;

3,设置缓存策略cachePolicy

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
NSURLRequestUseProtocolCachePolicy = 0,                //默认缓存策略
NSURLRequestReloadIgnoringLocalCacheData = 1,          // 忽略本地缓存数据,每次都从服务器加载数据,用在断点续传

// 以下两个选项,需要先判断用户的网络状态,如果没有连线,才会使用!
// 越来越多的应用,没有网络就不让用户使用!一定要让用户联网
NSURLRequestReturnCacheDataElseLoad = 2,               如果有缓存,就不去服务器加载,极少用
NSURLRequestReturnCacheDataDontLoad = 3,               只返回缓存,永远不去服务器加载  

NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:0 timeoutInterval:2.0];

定期处理缓存示例:
    if (缓存没有达到7) {
        request.cachePolicy = NSURLRequestReturnCacheDataElseLoad;
    }
    // 获得全局的缓存对象
    NSURLCache *cache = [NSURLCache sharedURLCache];
    if (缓存达到7) {
        [cache removeCachedResponseForRequest:request];
    }

4,缓存的使用注意

缓存看起来很美好,但需要谨慎使用。

如果请求某个URL的返回数据。

经常更新:不能用缓存!比如股票、彩票数据。

一成不变:果断用缓存。

偶尔更新:可以定期更改缓存策略 或者 清除缓存。

如果大量使用缓存,会越积越大,建议。

定期清除缓存。

四.ASI

1.缓存的使用步骤

1> 缓存单个请求

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// 1.获得全局的缓存对象(决定缓存的存储路径, 存储到什么地方)
ASIDownloadCache *cache = [ASIDownloadCache sharedCache];
// 设置默认的缓存加载策略
cache.defaultCachePolicy = ASIDoNotReadFromCacheCachePolicy;
 
// 2.设置请求对象的缓存对象(使用哪个缓存对象)
request.downloadCache = cache;
 
// 3.设置请求对象的缓存加载策略
request.cachePolicy = ASIOnlyLoadIfNotCachedCachePolicy;
// 如果没有缓存, 才发送请求
 
// 4.设置请求对象的缓存存储策略(存储的时长) 
request.cacheStoragePolicy = ASICachePermanentlyCacheStoragePolicy;// 永久存储

注意, 缓存加载策略的优先级 : request.cachePolicy > cache.defaultCachePolicy

2> 缓存所有请求

1
2
3
4
5
6
7
// 1.获得全局的缓存对象(决定缓存的存储路径, 存储到什么地方)
ASIDownloadCache *cache = [ASIDownloadCache sharedCache];
// 设置默认的缓存加载策略
cache.defaultCachePolicy = ASIOnlyLoadIfNotCachedCachePolicy;
 
// 2.设置全局缓存对象
[ASIHTTPRequest setDefaultCache:cache];

2.发送请求

1> 同步请求

1
[request startSynchronous];

2> 异步请求

1
[request startAsynchronous];

3.GET\POST

1> GET请求

1
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];

2> POST请求

1
2
3
4
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
// 添加普通参数(非文件参数)
[request setPostValue:@"zhangsan" forKey:@"username"];
[request setPostValue:@"123" forKey:@"pwd"];

4.文件下载

1
2
3
4
5
6
// 文件的存储路径(文件下载到什么地方)
request.downloadDestinationPath = filepath;
// 设置下载代理(监听下载进度)
request.downloadProgressDelegate = self.circleView;
// 支持断点下载
request.allowResumeForFileDownloads = YES;

5.文件上传

1
2
3
4
5
6
7
// 添加文件参数(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.监听请求的过程

1> 代理方法

1
2
3
4
5
6
7
8
9
// 设置代理
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

1
2
3
4
5
// 设置代理
request.delegate = self;
// 设置方法名
[request setDidStartSelector:@selector(start)]; // 开始发送请求, 就会调用代理的start方法
// ....

3> block

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
[request setStartedBlock:^{
    NSLog(@"setStartedBlock ----");
}];
 
[request setDataReceivedBlock:^(NSData *data) {
    NSLog(@"setDataReceivedBlock ----");
}];
 
[request setCompletionBlock:^{
    NSLog(@"setCompletionBlock ----");
}];
 
[self setFailedBlock:^{
    NSLog(@"setFailedBlock ----");
}];

7.通过request对象获得服务器的响应

1> 获得响应头信息

1
@property (atomic, retain) NSDictionary *responseHeaders;

2> 获得响应体(实体内容)

1
2
- (NSData *)responseData; // 直接返回服务器的二进制数据
- (NSString *)responseString; // 将二进制数据转成字符串(方便调试)

五.AFN

1.GET\POST

1> GET请求

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// 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) {
     NSLog(@"请求成功---%@", responseObject);
 }
 failure:^(AFHTTPRequestOperation *operation, NSError *error) {
     NSLog(@"请求失败---%@", error);
 }];

2> POST请求

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// 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) {
     NSLog(@"请求成功---%@", responseObject);
 }
 failure:^(AFHTTPRequestOperation *operation, NSError *error) {
     NSLog(@"请求失败---%@", error);
 }];

2.文件上传

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 1.获得请求管理者
AFHTTPRequestOperationManager *mgr = [AFHTTPRequestOperationManager manager];

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

[mgr POST:@"http://192.168.1.200:8080/MJServer/upload" parameters:params
 constructingBodyWithBlock:^(id<AFMultipartFormData> 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.Reachability

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 监听网络状态改变的通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkStateChange) name:kReachabilityChangedNotification object:nil];
 
// 创建Reachability
self.conn = [Reachability reachabilityForInternetConnection];
// 开始监控网络(一旦网络状态发生改变, 就会发出通知kReachabilityChangedNotification)
[self.conn startNotifier];
 
// 处理网络状态改变
- (void)networkStateChange
{
    // 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
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 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];

七.ASI和AFN有什么区别

1.性能(重点)

  • ASI基于底层的CFNetwork框架

  • AFN基于NSURLConnection

  • 运行性能: ASI > ASN

2.处理服务器数据

  1. AFN : 根据服务器返回数据的数据, 进行自动解析
    1. 服务器返回的是JSON数据, 自动转换为NSDictionary或者NSArray
    2. 服务器返回的是XML数据, 自动转换为NSXMLParser
  2. ASI : 并没有对服务器的数据进行解析, 直接返回NSData二进制数据
  3. 处理请求的过程
    1. AFN : success和failure两个block
    2. ASI : 有3种方式处理请求过程(代理方法\SEL\block)

3.ASI的特色(重点)

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

4.AFN的特色

  1. 使用简单
  2. 自带了网络监控功能

5. 网络小技巧

  1. 4G 100M bitps 600M Byte电影需要多长时间,6s* 8
  2. 将Safari浏览器改为开发者模式命令:defaults write com.apple.Safari IncludeDebugMenu 1
  3. http://127.0.0.1/
  4. href中 h表示超文本,ref表示引用