HTTP网络编程
1. 基本概念
- 客户端(Client):手机移动应用。
- 服务端(Server):为客户端提供服务、数据、资源的机器。
- 请求(Request):客户端向服务器索取数据的一种行为。
- 响应(Response):服务器对客户端的请求作出的反应(一般为返回数据给客户端)。
2. URL(Uniform Resource Locator:统一资源定位符)
- URL:就是资源的地址、位置。互联网上的每一个资源都有一个唯一的URL。
- 基本格式:协议://主机地址/路径
- 协议:不同的协议代表着不同的资源查找方式、资源传输方式。
- 主机地址:存放着资源的主机(服务器)的IP地址/域名。
- 路径:资源在主机中的具体位置。
3. HTTP协议
(1) 作用:
- 全称Hypertext Transfer Protocol:超文本传输协议。
- 规定客户端与服务器之间的数据传输格式。
- 让客户端和服务器有效的进行数据沟通。
(2) GET和POST:
- GET和POST的主要区别表现在数据传递上
- GET:1.在请求URL后面以“?”的形式跟上发给服务器的参数,多个参数之间用&隔开(如http://www.test.com/login?username=123&pwd=234);2.浏览器和服务器对URL长度有限制,URL可带参数受限制(通常不能超过1K)。
- POST:发给服务器的参数全部在请求体中(理论上,POST传递的数据量没有限制(具体看服务器的处理能力))。
- 推荐选择:1.POST:大量数据、安全性要求高、增删改查处理;2.GET:索取数据/数据查询。
(3) 通信过程
1. 请求:客户端向服务器索取数据。
2. 响应:服务器返回客户端相应的数据。
(4) 请求的方案
(5)常见响应吗
2.1 网络配置
2.2 NSURLConnect网络连接
- NSURL:设置请求地址
- NSURLRequest:创建请求对象(默认GET方式)
- NSURLConnect:网络连接对象
- NSMutableURLRequest:NSURLRequest子类,可用于修改请求方式
- didFailWithError:错误处理协议
- didReceiveData:获取数据协议
- connectionDidFinishLoading:加载数据完成协议
(1)NSMutableURLRequest常用方法:
#import "NSConnectionViewController.h"
#define LIST_URL @"http://new.api.bandu.cn/book/listofgrade?grade_id=2"
//NSURLConnectionDataDelegate:连接服务器的普通代理协议,作为错误处理等协议完成;
//NSURLConnectionDelegate:连接服务器对象的数据代理协议,当回传数据时使用的协议;
@interface NSConnectionViewController ()<NSURLConnectionDataDelegate, NSURLConnectionDelegate>
{
//定义一个URL连接对象,通过网络地址,可以进行连接工作
NSURLConnection *_connect;
//接受服务器传回的数据
NSMutableData *_data;
}
@end
@implementation NSConnectionViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.view setBackgroundColor:[UIColor whiteColor]];
_data = [[NSMutableData alloc] init];
// Do any additional setup after loading the view.
NSArray *arr = @[@"同步请求", @"block异步", @"delegate异步"];
NSUInteger count = [arr count];
for(int i=0; i<count; i++){
UIButton *btn = [UIButton buttonWithType: UIButtonTypeSystem];
btn.frame = CGRectMake(80, 70+i*40, 50, 30);
[btn addTarget:self action:@selector(btnAction:) forControlEvents:UIControlEventTouchDown];
[btn setTitle:arr[i] forState:UIControlStateNormal];
btn.tag = 100 + i;
[btn sizeToFit];
[self.view addSubview: btn];
}
}
- (void)btnAction :(UIButton *)sender
{
// 1.创建NSURL对象
NSURL *url = [NSURL URLWithString:LIST_URL];
// 2.创建NSURLRequst 默认GET
NSURLRequest *request = [NSURLRequest requestWithURL: url];
switch (sender.tag) {
case 100: //同步请求
{
NSError *error = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&error];
if(error == nil){
NSLog(@"request: %@", request);
NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"---%@", str);
}
else{
NSLog(@"--错误信息: %@", error);
}
NSLog(@"线程:%@", [NSThread currentThread]);
break;
}
case 101: //block方式异步请求
{
//[NSOperationQueue mainQueue]为主队列
//[[NSOperationQueue alloc] init]子队列
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
if(connectionError == nil){
NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"str = %@", str);
}
else{
NSLog(@"error = %@", connectionError);
}
//查看线程
NSLog(@"currentThread = %@", [NSThread currentThread]);
}];
break;
}
case 102: //delegate请求
{
//创建一个网络连接对象
//参数一:连接的请求对象
//参数二:代理对象,用来实现回传数据的代理协议
_connect = [NSURLConnection connectionWithRequest: request delegate:self];
break;
}
default:
break;
}
}
#pragma mark -
#pragma mark -- Delegate --
//处理错误信息的代理协议
//如果有任何连接错误,调用此协议,运行错误的打印查看
//P1:连接对象
//P2:错误信息
- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
NSLog(@"error: = %@", error);
}
//处理服务器的响应码
- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
//将响应码转化为http响应码
NSHTTPURLResponse *res = (NSHTTPURLResponse *) response;
switch (res.statusCode) {
case 200:
NSLog(@"连接成功!服务器政策!");
break;
case 400:
NSLog(@"客户端请求语法错误,服务器无法解析");
break;
case 404:
NSLog(@"服务器正常开启,没有找到连接地址网页或数据");
break;
case 500:
NSLog(@"服务器宕机或关机");
break;
default:
break;
}
}
//接受服务器回传数据时调用
-(void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
//将每次回传的数据连接起来
[_data appendData:data];
}
- (void) connectionDidFinishLoading:(NSURLConnection *)connection{
//将二进制数据转化为字符串(解析)
NSLog(@"baidu page = %@", [[NSString alloc] initWithData:_data encoding:NSUTF8StringEncoding]);
//查看线程
NSLog(@"currentThread = %@", [NSThread currentThread]);
}
2.3 NSURLSession网络连接
(1)使用步骤
- 1.创建NSURLSession的会话
- 2.根据会话创建Task
- 3.执行Task
(2)常见方法
- suspend:暂停
- resume:恢复
- cancel:取消
- cancelByProducingResumeData:(void(^)………):取消下载任务时使用
#import "NSURLSessionViewController.h"
#define LIST_URL @"http://new.api.bandu.cn/book/listofgrade?grade_id=2"
#define IMAGE_URL @"http://pic33.nipic.com/20130916/3420027_192919547000_2.jpg"
#define VIDER_URL @"http://war3down1.uuu9.com/war3/201609/201609131028.rar"
@interface NSURLSessionViewController ()<
NSURLSessionDataDelegate,
NSURLSessionDownloadDelegate
>
{
//接收数据
NSMutableData *_data;
UIImageView *imageView;
//区分下载
BOOL isDonwloadImage;
}
@property (nonatomic, strong) NSURLSession *session;
@property (nonatomic, strong) NSURLSessionDownloadTask *downloadTask;
@property (nonatomic, strong) NSData *downloadData; //已下载的数据量
@end
@implementation NSURLSessionViewController
-(NSURLSession *)session{
if(!_session){
_session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc] init]];
}
return _session;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self.view setBackgroundColor:[UIColor whiteColor]];
self.downloadData = [[NSData alloc] init];
imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 400, 200, 100)];
NSArray *arr = @[@"POST", @"GET", @"Delegate", @"donwload", @"开始下载", @"暂停下载", @"继续下载", @"取消下载"];
NSUInteger count = [arr count];
for(int i=0; i<count; i++){
UIButton *btn = [UIButton buttonWithType: UIButtonTypeSystem];
btn.frame = CGRectMake(80, 100+i*50, 50, 40);
[btn setTitle:arr[i] forState:UIControlStateNormal];
btn.tag = 100 + i;
[btn sizeToFit];
[btn addTarget:self action:@selector(btnAction:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview: btn];
}
// Do any additional setup after loading the view.
}
-(void)btnAction : (UIButton *)btn
{
switch (btn.tag) {
case 100: //post
[self POST];
break;
case 101: //GET
[self GET];
break;
case 102: //delegate
[self delegateRequest];
break;
case 103: //download
[self downloadImage];
break;
case 104: //start download
{
isDonwloadImage = NO;
//根据会话创建下载任务
self.downloadTask = [self.session downloadTaskWithURL:[NSURL URLWithString: VIDER_URL]];
//启动任务
[self.downloadTask resume];
break;
}
case 105: //Pause download
//可以继续恢复下载
[self.downloadTask suspend];
break;
case 106: //continue download
{
//如果在暂停时候没有保存数据则不需要下面语句
// self.downloadTask = [self.session downloadTaskWithResumeData: self.downloadData];
[self.downloadTask resume];
break;
}
case 107: //stop download
{
//取消任务,不能继续下载
[self.downloadTask cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
//resumeData 代表当前下载了多少数据
//如果不需要保存数据则无需赋值
// self.downloadData = resumeData;
}];
break;
}
default:
break;
}
}
//POST请求方式
-(void)POST
{
//设置地址
NSURL *url = [NSURL URLWithString: @"http://new.api.bandu.cn/book/listofgrade"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: url];
//设置请求方式
request.HTTPMethod = @"POST";
//设置请求体
request.HTTPBody = [@"grade_id=2" dataUsingEncoding:NSUTF8StringEncoding];
// 1.创建Session
NSURLSession *session = [NSURLSession sharedSession];
// 2.根据会话创建任务
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"线程: %@", [NSThread currentThread]);
if(error == nil)
{
//解析数据
NSDictionary *jsoDic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error: &error];
NSLog(@"%@", jsoDic);
}
//回归到主线程
NSLog(@"POST完毕");
}];
// 3.启动任务
[dataTask resume];
}
//GET请求方式
- (void)GET
{
//设置地址
NSURL *url = [NSURL URLWithString: LIST_URL];
//封装一个请求类
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// 1.创建Session
NSURLSession *session = [NSURLSession sharedSession];
// 2.根据会话创建任务
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"线程: %@", [NSThread currentThread]);
if(error == nil)
{
//解析数据
NSDictionary *jsoDic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error: &error];
NSLog(@"%@", jsoDic);
}
else{
NSLog(@"error: %@", error);
}
//回归到主线程
NSLog(@"GET完毕");
}];
// 3.启动任务
[dataTask resume];
}
//使用协议来请求数据
-(void)delegateRequest
{
isDonwloadImage = YES;
// 设置地址
NSURL *url = [NSURL URLWithString: LIST_URL];
//封装一个请求类
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// 1.创建Session
/*
+ (NSURLSessionConfiguration *)defaultSessionConfiguration; 默认
+ (NSURLSessionConfiguration *)ephemeralSessionConfiguration; 无痕浏览
+ (NSURLSessionConfiguration *)backgroundSessionConfigurationWithIdentifier:(NSString *)iddentifier 下载任务并通知
*/
//delegateQueue:协议在哪个线程执行
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
delegate:self
delegateQueue:[NSOperationQueue mainQueue]];
//根据会话创建任务
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request];
//启动任务
[dataTask resume];
}
#pragma mark -
#pragma mark -- Delegate --
//接收服务器的响应
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
NSLog(@"didReceiveResponse");
if(_data == nil){
_data = [[NSMutableData alloc] init];
}
else{
_data.length = 0;
}
//NSURLSessionResponseDisposition:
// NSURLSessionResponseCancel = 0, //请求后不接受服务器数据,默认
// NSURLSessionResponseAllow = 1, //允许接受服务器的数据
// NSURLSessionResponseBecomeDownload //转成下载任务
// NSURLSessionResponseBecomeStream NS_ENUM_AVAILABLE(10_11, 9_0) = 3, //转成流
completionHandler(NSURLSessionResponseAllow);
}
//接收到数据,该方法会调用调用多次
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
NSLog(@"didReceiveData");
//拼接数据
[_data appendData:data];
NSLog(@"%@", _data);
}
// 数据请求完成/请求错误调用的方法
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
if(error == nil){
if(!isDonwloadImage){
NSLog(@"下载任务完成");
}
else{
id jsoDic = [NSJSONSerialization JSONObjectWithData:_data options:NSJSONReadingMutableContainers error:nil];
NSLog(@"%@", jsoDic);
}
NSLog(@"delegate请求完毕");
}
else{
NSLog(@"error = %@", error);
}
}
- (void)downloadImage
{
//1.会话
NSURLSession *session = [NSURLSession sharedSession];
//2.根据会话创建任务
//location:本地文件url的位置
NSURLSessionDownloadTask *Dask = [session downloadTaskWithURL:[NSURL URLWithString:IMAGE_URL] completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
//在子线程中运行
if(error == nil)
{
NSLog(@"下载图片中");
imageView.image = [UIImage imageWithData:[NSData dataWithContentsOfURL: location]];
[self.view addSubview: imageView];
}
}];
//启动
[Dask resume];
}
#pragma mark -
#pragma mark -- 下载任务协议 --
//下载的进度
// bytesWritten:当前次下载的数据大小
// totalBytesWritten 总共下载了多少数据量
// totalBytesExpectedToWrite 总数据的大小
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
NSLog(@"已经下载了%f", 1.0 * totalBytesWritten / totalBytesExpectedToWrite);
}
// 恢复任务调用
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
{
NSLog(@"恢复了下载任务");
}
//下载完之后 文件所在位置
-(void) URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
NSLog(@"下载位置:%@", location);
}
2.4 AFNetworking
- AFHTTPSSessionManager:HTTP请求管理器
- Success:responseObject:请求数据成功
- Failure:error:请求数据失败
- AFNetworkReachabilityManager:网络连接监测管理器(查看手机连接网络状态)
- AFURLRequestSerialization:请求的数据格式(默认二进制)
- AFURLResponseSerialization:响应的数据格式(默认JSON格式)
#import "AFNetworkingViewController.h"
#import "AFNetworking.h"
#import "Reachability.h"
#define BOOK_URL @"http://new.api.bandu.cn/book/listofgrade?grade_id=2"
#define IMAGE_URL @"http://pic33.nipic.com/20130916/3420027_192919547000_2.jpg"
@interface AFNetworkingViewController ()
@property (nonatomic, strong) Reachability *readch;
@end
@implementation AFNetworkingViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
NSArray *arr = @[@"AFNetwork监测网络状态", @"发送请求", @"下载任务", @"开启网络监测"];
NSUInteger count = [arr count];
for(int i=0; i<count; i++)
{
UIButton *btn = [UIButton buttonWithType: UIButtonTypeSystem];
btn.frame = CGRectMake(80, 80+i*60, 50, 30);
[btn setTitle:arr[i] forState:UIControlStateNormal];
btn.tag = 100 + i;
[btn sizeToFit];
[btn addTarget:self action:@selector(btnAction:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview: btn];
}
// Do any additional setup after loading the view.
}
- (void)btnAction :(UIButton *)btn
{
switch (btn.tag) {
case 100:
[self AFNetMonitor];
break;
case 101:
[self AFGetData];
break;
case 102:
[self downloadImage];
break;
case 103:
[self reachbility];
break;
default:
break;
}
}
#pragma mark -
#pragma mark -- AFNetwork网络监测 --
- (void)AFNetMonitor
{
//检查网络连接的状态
//AFNetworkReachabilityManager 网络连接监测管理类
//开启网络状态监测器
//shareManager:获得唯一的单例对象
[[AFNetworkReachabilityManager sharedManager] startMonitoring];
//获取网络连接的结果
[[AFNetworkReachabilityManager sharedManager]
setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
switch (status) {
case AFNetworkReachabilityStatusNotReachable:
NSLog(@"没有网络连接");
break;
case AFNetworkReachabilityStatusReachableViaWiFi:
NSLog(@"WIFI连接");
break;
case AFNetworkReachabilityStatusReachableViaWWAN:
NSLog(@"通过移动网络,4G");
break;
default:
NSLog(@"无法知道网络类型");
break;
}
}];
}
#pragma mark -
#pragma mark -- 苹果官方监测网络 --
- (void)reachbility
{
NSLog(@"开启网络监测");
// 监听通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changeReadchability) name:kReachabilityChangedNotification object:nil];
//开启网络监控
self.readch = [Reachability reachabilityForInternetConnection];
[self.readch startNotifier];
}
- (void)changeReadchability
{
// NotReachable = 0,
// ReachableViaWiFi = 2,
// ReachableViaWWAN = 1
switch (self.readch.currentReachabilityStatus) {
case NotReachable:
NSLog(@"没有网络");
break;
case ReachableViaWWAN:
NSLog(@"手机连接");
break;
case ReachableViaWiFi:
NSLog(@"WIFI连接");
break;
default:
break;
}
}
#pragma mark -
#pragma mark -- AFNetwork请求数据 --
- (void) AFGetData
{
//创建http连接管理对象
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
//响应序列化(响应接收到的数据)
// AFXMLParserResponseSerializer: XML数据
// AFJSONResponseSerializer: JSON数据
// AFHTTPResponseSerializer: 二进制数据
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
//转换编码
// NSString *str = [BOOK_URL stringByAddingPercentEncodingWithAllowedCharacters: [NSCharacterSet URLQueryAllowedCharacterSet]];
//请求体
NSDictionary *dict = @{@"grade_id":@"2"};
/* GET 方法获取服务器的数据
GET 通过get方法
P1:参数传入一个url对象
P2:通过制定的解构传入参数
P3:指定下载的进度条UI
P4:下载成功数据后调用此b语法块(PP1,下载的人物线程,pp2返回的数据内容)
P5:下载失败后调用此语法块(pp1.下载的任务线程,pp2返回的错误类型) */
[manager GET:BOOK_URL parameters:dict progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"请求成功 ");
NSError *error = nil;
NSDictionary *jsonDic = [NSJSONSerialization JSONObjectWithData:responseObject
options:NSJSONReadingMutableLeaves
error:&error];
if(error == nil){
NSLog(@"%@", jsonDic);
}
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"请求失败! error = %@", error);
}];
}
#pragma mark -
#pragma mark -- AFNetwork download file --
-(void)downloadImage
{
//定义管理器
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
// 下载任务
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:IMAGE_URL]];
__block NSProgress *progresst = nil;
// totalUnitCount 总共下载量
// completedUnitCount 已下载的数据
// progress:监测下载进度
// block回调
// targetPath下载后的位置
NSURLSessionDownloadTask *task = [manager downloadTaskWithRequest:request progress:^(NSProgress * _Nonnull downloadProgress){
//监测下载进度
progresst = downloadProgress;
// KVO监测进度
[progresst addObserver:self forKeyPath:@"completedUnitCount" options:NSKeyValueObservingOptionNew context:nil];
} destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
NSLog(@"下载的位置%@", targetPath);
//存放到沙盒
NSString *location = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:response.suggestedFilename];
//返回一个你想让下载的文件放置的位置
return [NSURL fileURLWithPath: location];
} completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
NSLog(@"filePath = %@", filePath);
NSLog(@"error = %@", error);
}];
[task resume];
}
//监测下载进度
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
NSProgress *progress = (NSProgress *)object;
NSLog(@"已经下载的进度为:%f", 1.0 * progress.completedUnitCount / progress.totalUnitCount);
}