�Http协议-超文本传输协议
Http协议是应用层协议,底层要求的传输协议必须是可靠的传输协议,通常是TCP协议。
Http协议规定客户端与服务端之间的通讯方式为一次请求一次响应,即客户端发起链接,并向服务端发送一个标准的Http请求,而后服务端处理该请求后给予一个标准的Http响应,所以对于Http协议而言,服务端永远不会主动响应客户端。
Http协议现在常用的是Http1.1版本协议,在Http1.0版本时,要求一次会话只能完成一次请求与响应。即客户端与服务端建立TCP链接后,发送一次请求,当服务端给予响应后立即断开链接。在Http1.1版本中,一个较大的改动就是在一次会话中可以完成多次请求与响应。
Http协议中规定的请求与响应的内容大部分是文本数据,但是这些字符只能是ISO88590-1编码中出现的字符,这意味着在Http协议中是不能直接出现中文等字符的。
Http中的请求定义Request,一个请求通常包含三部分:请求行,消息头,消息正文。
请求行的格式为: Method(请求方式) URL(请求的资源路径) Protocol(协议版本)
消息头是客户端发起请求时传递过来的一些附加信息,比如描述客户端是谁,以及希望与客户端如何通讯,描述消息征文的长度以及内容类型等信息。消息头由若干行组成,每一行为一个消息头,格式为Name :Value(CRLF)格式,当所有消息头全部发送完毕后会单独发送一个CRLF。
消息正文是二进制数据,内容是用户实际向服务端传递的数据。它可能是用户传递的注册用户的信息,上传的附件内容等。一个请求中可以不含有消息正文部分。请求中是否含有消息正文的一个重要标志是请求的消息头中是否含有以下两个头:
- Content - Type :用于说明消息正文的内容是什么类型的数据。
- Content - Length : 用于说明消息正文的数据长度(字节量)。
Http响应是服务器端发送给客户端的内容。一个响应应当包含三部分:状态行,响应头,响应正文。
状态行格式分为三部分:Protocol(协议版本) Status_code(状态代码) Status_reason(状态描述) (CRLF)
状态代码是一个3位数字,分为5类:
- 1XX :消息,在Http1.0协议时为保留部分,未使用。
- 2XX :成功,表示客户端请求成功
- 3XX:重定向,表示客户端需要进一步操作后才能完成请求
- 4XX:客户端错误,表示客户端的请求无效
- 5XX:服务端错误,表示请求被接受,但是服务器处理时发生了错误。
常见状态代码:
- 200 : 请求已接受,并正常响应客户端。
- 302 : 要求客户端进一步请求服务端指定的路径
- 404 : 客户端请求的资源未找到
- 500 : 服务端发生错误
响应头的格式与意义同请求中的信息头一样。
响应正文也是二进制数据,是服务端响应给客户端的实体数据,通常就是客户端所请求的资源。一个响应中是否含有响应正文可以通过响应头中的两个信息得知:
- Content - Type : 说明响应正文的数据类型。
- Content- Length :说明响应正文的长度(字节量)。
客户端就是通过这两个头来读取并理解响应正文内容的。
NSURLSession
NSURLSession派生自NSObject,是用来协调一组相关网络数据传输任务的对象。NSURLSession类和相关类提供了一个API,用于从url指示的端点下载数据并将数据上传到该端点。当你的应用没有运行时,或者在iOS中,当你的应用被挂起时,API还允许你的应用执行后台下载。提供一组丰富的委托方法支持身份验证,并允许应用程序收到重定向等事件的通知。
NSURLSession API涉及许多不同的类,它们以一种相当复杂的方式协同工作,如果阅读参考文档,可能就不太容易理解。在使用API之前,阅读URL加载系统主题中的概述。第一步、上传和下载小节中的文章提供了使用NSURLSession执行常见任务的示例。
使用NSURLSession API,应用程序创建一个或多个会话,每个会话协调一组相关的数据传输任务。例如,如果正在创建一个web浏览器,应用程序可能会为每个选项卡或窗口创建一个会话,或者为交互使用创建一个会话,为后台下载创建另一个会话。在每个会话中,应用程序将添加一系列任务,每个任务表示对特定URL的请求(如果需要,将遵循HTTP重定向)。
NSURLSession常用属性
@property (class, readonly, strong) NSURLSession *sharedSession;
属性描述 : 共享的单例会话对象。对于基本请求,NSURLSession类提供了一个共享的单例会话对象,为创建任务提供了一个合理的默认行为。只需几行代码,就可以使用共享会话将URL的内容提取到内存中。与其他会话类型不同,创建共享会话;只需通过直接使用此属性来访问它。因此,不需要提供委托或配置对象。。
@property (class, readonly, strong) NSURLSession *sharedSession;
例如通过单例初始化网络数据传输任务对象:
//通过单例初始化网络数据传输任务对象
NSURLSession *session = [NSURLSession sharedSession];
NSURLSession常用函数
- (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url completionHandler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))completionHandler;
函数描述 : 创建检索指定URL内容的任务,然后在完成时调用处理程序。创建任务后,必须通过调用其resume方法启动任务。通过使用完成处理程序,该任务将绕过对用于响应和数据传递的委托方法的调用,而是在完成处理程序中提供任何生成的NSData、NSURLResponse和NSError对象。但是,仍然调用用于处理身份验证的委托方法。仅当在其委托包含URLSession:dataTask:didReceiveData:方法的会话中创建任务时,才应传递nil完成处理程序。
如果请求成功完成,则完成处理程序块的data参数包含资源数据,error参数为nil。如果请求失败,则数据参数为nil, error参数包含关于失败的信息。如果接收到来自服务器的响应,无论请求是否成功完成或失败,response参数都包含该信息。
参数 :
url : 要检索的URL。
completionHandler : 加载请求完成时调用的完成处理程序。此处理程序在委托队列上执行。如果传递nil,则在任务完成时只调用会话委托方法,使此方法等效于dataTaskWithRequest:方法。此完成处理程序接受以下参数:
- data :服务器返回的数据。
- response : 提供响应元数据(如HTTP头和状态代码)的对象。如果您正在发出HTTP或HTTPS请求,则返回的对象实际上是NSHTTPURLResponse对象。
- error : 一个错误对象,指示请求失败的原因;如果请求成功,则为nil。
返回值 : 新会话数据任务。
- (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url completionHandler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))completionHandler;
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))completionHandler;
函数描述 : 创建基于指定的URL请求对象检索URL内容的任务,并在完成时调用处理程序。通过基于请求对象创建任务,可以调整任务行为的各个方面,包括缓存策略和超时间隔。
通过使用完成处理程序,该任务将绕过对用于响应和数据传递的委托方法的调用,而是在完成处理程序中提供任何生成的NSData、NSURLResponse和NSError对象。但是,仍然调用用于处理身份验证的委托方法。仅当在其委托包含URLSession:dataTask:didReceiveData:method的会话中创建任务时,才应传递nil完成处理程序。
创建任务后,必须通过调用其resume方法启动任务。如果请求成功完成,则完成处理程序块的data参数包含资源数据,error参数为nil。如果请求失败,则data参数为nil,error参数包含有关失败的信息。如果接收到来自服务器的响应,无论请求是成功完成还是失败,response参数都包含该信息。
参数 :
request : 提供URL、缓存策略、请求类型、正文数据或正文流等的URL请求对象。
completionHandler : 加载请求完成时要调用的完成处理程序。此处理程序在委托队列上执行。
如果传递nil,则在任务完成时仅调用会话委托方法,使此方法等效于dataTaskWithRequest:方法。此完成处理程序接受以下参数:
- data :服务器返回的数据。
- response :提供响应元数据(如HTTP头信息和状态代码)的对象。如果您正在发出HTTP或HTTPS请求,返回的对象实际上是一个NSHTTPURLResponse对象。
- error :指示请求失败原因的错误对象,如果请求成功则为nil。
返回值 : 新的会话数据任务。
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))completionHandler;
例如通过基于请求对象创建请求任务的简单代码片段:
//初始化Url
NSURL *url = [[NSURL alloc]initWithString:@"https://www.test.68mall.com/index"];
//可变URL加载请求
NSMutableURLRequest *request = [[NSMutableURLRequest alloc]initWithURL:url];
//设置请求方式(默认也是GET)
[request setHTTPMethod:@"GET"];
//设置请求头
request.allHTTPHeaderFields = @{@"Content-Type":@"application/x-www-form-urlencoded",@"User-Agent":@"szyapp/ios"};
//通过单例初始化网络数据传输任务对象
NSURLSession *session = [NSURLSession sharedSession];
//通过基于请求对象创建请求任务
[[session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if(error == nil){
//请求成功
//将响应数据转换为字典
NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
NSLog(@"%@",jsonDict);
}else{
NSLog(@"%@",error);
}
}]resume];
注:NSLocalizedDescription=The resource could not be loaded because the App Transport Security policy requires the use of a secure connection(译:NSLocalizedDescription=无法加载资源,因为应用传输安全策略要求使用安全连接)错误可能是需要发送https请求,结果发送http请求导致的。
- (void)resume;
函数描述 : 如果任务已挂起,则继续执行该任务。新初始化的任务以挂起状态开始,因此需要调用此方法来启动任务。
- (void)resume;
NSURLRequest
独立于协议或URL方案的URL加载请求。NSURLRequest封装了加载请求的两个基本属性:要加载的URL和用于加载它的策略。此外,对于HTTP和HTTPS请求,URLRequest包括HTTP方法(GET、POST等)和HTTP头。最后,如自定义协议属性中所述,自定义协议可以支持自定义属性。NSURLRequest的可变子类是NSMutableURLRequest。
NSURLRequest仅表示有关请求的信息。使用其他类(如NSURLSession)将请求发送到服务器。
NSMutableURLRequest
独立于协议或URL模式的可变URL加载请求。NSMutableURLRequest是NSURLRequest的一个子类,它允许更改请求的属性。
NSMutableURLRequest仅表示有关请求的信息。使用其他类(如NSURLSession)将请求发送到服务器。
基于请求创建网络操作的类对该请求进行深拷贝。因此,在创建网络操作之后更改请求对正在进行的操作没有影响。例如,如果您使用dataTaskWithRequest:completionHandler:从请求创建数据任务,然后稍后更改请求,那么数据任务将继续使用原始请求。
NSMutableURLRequest常用属性
@property (nullable, copy) NSURL *URL;
属性描述 :正在请求的URL。
@property (nullable, copy) NSURL *URL;
例如:通过属性设置Url
[request setURL:url];
@property (copy) NSString *HTTPMethod;
属性描述 : HTTP请求方法。默认的HTTP方法是“GET”。
@property (copy) NSString *HTTPMethod;
例如:通过属性设置请求方式
[request setHTTPMethod:@"POST"];
@property (nullable, copy) NSData *HTTPBody;
属性描述 : 请求主体。请求体作为请求的消息体发送,就像在HTTP POST请求中一样。设置HTTP主体数据清除HTTPBodyStream中的任何输入流。这些值是互斥的。
@property (nullable, copy) NSData *HTTPBody;
例如:通过属性设置请求主体
//字符串格式化百度key与百度语音key
NSString *valueStr = [NSString stringWithFormat:@"grant_type=client_credentials&client_id=%@&client_secret=%@",BaiduKey,BaiduSpeechSecretKey];
//返回一个NSData对象,该对象包含使用给定编码编码的接收器的表示形式。
NSData *postData = [valueStr dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
//设置请求主体
[request setHTTPBody:postData];
@property (nullable, copy) NSDictionary<NSString *, NSString *> *allHTTPHeaderFields;
属性描述 : 包含所有HTTP请求头中字段的字典。某些请求头字段是保留的(请参阅保留的HTTP请求头)。不要使用此属性设置此类请求头字段。
@property (nullable, copy) NSDictionary<NSString *, NSString *> *allHTTPHeaderFields;
例如:使用属性设置请求头
//application/x-www-form-urlencoded : 数据被编码为名称/值对,标准编码格式
request.allHTTPHeaderFields = @{@"Content-Type":@"application/x-www-form-urlencoded",@"User-Agent":@"szyapp/ios"};
NSURLResponse
与URL加载请求的响应相关联的元数据,独立于协议和URL方案。相关的NSHTTPURLResponse类是NSURLResponse的一个常用子类,它的对象表示对HTTPURL加载请求的响应,并存储额外的协议特定信息,如响应头。每当发送一个HTTP请求,你得到的NSURLResponse对象实际上是NSHTTPURLResponse类的一个实例。NSURLResponse对象不包含表示URL内容的实际字节。相反,通过代理调用一次返回一部分数据,或者在请求完成时返回全部数据,这取决于用于发起请求的方法和类。
NSURLResponse常用属性
@property (nullable, readonly, copy) NSURL *URL;
属性描述 : 响应的URL。
@property (nullable, readonly, copy) NSURL *URL;
@property (nullable, readonly, copy) NSString *MIMEType;
属性描述 : 响应的MIME类型。MIME类型通常由响应的原始源提供。但是,如果可以确定响应的源不正确地报告了信息,则协议实现可以更改或纠正该值。如果响应的原始源没有提供MIME类型,则可能会尝试猜测MIME类型。
@property (nullable, readonly, copy) NSString *MIMEType;
@property (readonly) long long expectedContentLength;
属性描述 : 响应内容的预期长度。如果不能确定长度,这个属性的值是NSURLResponseUnknownLength。一些协议实现报告内容长度作为响应的一部分,但并不是所有协议都保证交付这么多数据。应用程序应该准备好处理或多或少的数据。
@property (readonly) long long expectedContentLength;
@property (nullable, readonly, copy) NSString *textEncodingName;
属性描述 : 由响应的原始源提供的文本编码的名称。如果协议没有提供文本编码,该属性的值为nil。可以通过调用CFStringConvertIANACharSetNameToEncoding将此字符串转换为CFStringEncoding值。随后可以通过调用CFStringConvertEncodingToNSStringEncoding将该值转换为NSStringEncoding值。
@property (nullable, readonly, copy) NSString *textEncodingName;
例如
//通过基于请求对象创建请求任务
[[session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if(error == nil){
//请求成功
NSLog(@"%@",response.textEncodingName);
//返回与给定IANA注册表“字符集”名称最接近的映射的CoreFoundation编码常量。如果名称无法识别,则返回kCFStringEncodingInvalidId常量。
CFStringEncoding cfEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef) [response textEncodingName]);
//返回与给定的CoreFoundation编码常量最接近的Cocoa编码常量。
NSStringEncoding encoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding);
//通过使用编码将数据中的字节转换为UTF-16代码单元而初始化的NSString对象
NSString *text = [[NSString alloc] initWithData:data encoding:encoding];
NSLog(@"%@",text);
}else{
NSLog(@"%@",error);
}
}]resume];
@property (nullable, readonly, copy) NSString *suggestedFilename;
属性描述:响应数据的建议文件名。访问此属性将尝试使用以下信息生成文件名,顺序如下:
- 使用内容处置标头指定的文件名。
- URL的最后一个路径组件。
- URL的主机。
- 如果URL的主机无法转换为有效的文件名,则使用文件名“unknown”。
在大多数情况下,此属性会根据MIME类型附加适当的文件扩展名。无论资源是否保存到磁盘,访问此属性始终返回有效的文件名。
@property (nullable, readonly, copy) NSString *suggestedFilename;
NSHTTPURLResponse
NSHTTPURLResponse类是NSURLResponse的一个子类,它提供了访问特定于HTTP协议响应的信息的方法。无论何时发出HTTP URL加载请求,从NSURLSession、NSURLConnection或NSURLDownload类返回的任何响应对象都是NSHTTPURLResponse类的实例。
NSHTTPURLResponse常用属性
@property (readonly) NSInteger statusCode;
属性描述 : 响应的HTTP状态代码。
@property (readonly) NSInteger statusCode;
@property (readonly, copy) NSDictionary *allHeaderFields;
属性描述 : 所有的HTTP响应头字段。此属性的值是一个字典,其中包含作为服务器响应的一部分接所收到的所有HTTP响应头字段。通过检查这个字典,客户端可以看到HTTP服务器返回的“原始”响应头信息。这个字典中的键是响应头字段名,从服务器接收到的。
@property (readonly, copy) NSDictionary *allHeaderFields;
NSHTTPURLResponse常用函数
- (nullable NSString *)valueForHTTPHeaderField:(NSString *)field API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0));
函数描述 : 返回与给定响应头字段对应的值。与HTTP RFC保持一致,HTTP响应头字段名不区分大小写。
参数 :
field : 要检索的响应头字段的名称。名称不区分大小写。
返回值 : 与给定响应头字段关联的值,如果没有值与字段关联,则为nil。
- (nullable NSString *)valueForHTTPHeaderField:(NSString *)field API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0));
+ (NSString *)localizedStringForStatusCode:(NSInteger)statusCode;
函数描述 : 返回与指定的HTTP状态代码相对应的本地化字符串。
参数 :
statusCode : HTTP状态代码。详见RFC 2616。
返回值 :适合向用户显示的描述指定状态代码的本地化字符串。
+ (NSString *)localizedStringForStatusCode:(NSInteger)statusCode;
请求百度语音识别REST的练习代码片段:
//
// TextCodeController.m
// FrameworksTest
#import "TestCodeController.h"
#import <AVFoundation/AVFoundation.h>
@interface TestCodeController ()
@property (nonatomic, strong) AVPlayer *player;
@end
@implementation TestCodeController
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.title = @"测试代码控制器";
[self playContent:@"中国加油"];
}
- (void)playContent:(NSString *)content{
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:[NSURL URLWithString:[self requestBaiDuREST:content]] options:nil];
AVPlayerItem *songitem = [[AVPlayerItem alloc] initWithAsset:asset];
self.player = [[AVPlayer alloc] initWithPlayerItem:songitem];
//播放音量
self.player.volume = 1;
//添加观察者
[songitem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
}
///观察者执行的函数
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if ([keyPath isEqualToString:@"status"]) {
AVPlayerItem *item = (AVPlayerItem *)object;
if (item.status == AVPlayerItemStatusReadyToPlay) {
//开始播放当前项
[self.player play];
}
}
}
///请求百度语音识别REST
- (NSString *)requestBaiDuREST:(NSString *)content{
NSString *unencodedString = [self URLEncodedString:content];
static NSString * const BaiduKey = @"不给你们看";
static NSString * const BaiduSpeechSecretKey = @"这个也不给你们看";
//路径
NSURL *url = [[NSURL alloc]initWithString:@"https://aip.baidubce.com/oauth/2.0/token"];
//可变URL加载请求
NSMutableURLRequest *request = [[NSMutableURLRequest alloc]init];
//设置要请求的路径
[request setURL:url];
//设置HTTP请求方式
[request setHTTPMethod:@"POST"];
//字符串格式化百度key与百度语音key
NSString *valueStr = [NSString stringWithFormat:@"grant_type=client_credentials&client_id=%@&client_secret=%@",BaiduKey,BaiduSpeechSecretKey];
//返回一个NSData对象,该对象包含使用给定编码编码的接收器的表示形式。
NSData *postData = [valueStr dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
//设置请求主体
[request setHTTPBody:postData];
//纪录completionHandler代码块中的异常
NSError __block *blockError = nil;
//纪录completionHandler代码块中的数据
NSData __block *blockdData = nil;
//纪录completionHandler代码块中的响应对象
NSURLResponse __block *URLResponse = nil;
//使用初始值创建新的计数信号量。
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
NSURLSession *session = [NSURLSession sharedSession];
[[session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
blockError = error;
blockdData = data ;
URLResponse = response;
//信号量(递增),唤醒线程
dispatch_semaphore_signal(semaphore);
}] resume];
//等待(减少)信号量。阻塞线程
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
//如果blockdData为nil说明发生了异常
if (blockdData == nil) {
NSLog(@"send request failed: %@", blockError);
}else{
//将blockdData以NSUTF8StringEncoding解析为字符串
NSString *receiveStr = [[NSString alloc]initWithData:blockdData encoding:NSUTF8StringEncoding];
//返回以NSUTF8StringEncoding编码NSData对象
NSData * datas = [receiveStr dataUsingEncoding:NSUTF8StringEncoding];
//将datas转换为字典
NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:datas options:NSJSONReadingMutableLeaves error:nil];
//如果字典中有access_token对应的值
if([[jsonDict objectForKey:@"access_token"] length] > 0){
//字符串格式化access_token对应的值
NSString *valueStr = [NSString stringWithFormat:@"tex=%@&tok=%@&cuid=013473005841386&ctp=1&lan=zh",unencodedString,[jsonDict objectForKey:@"access_token"]];
NSURLSession *session = [NSURLSession sharedSession];
[[session dataTaskWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"https://tsn.baidu.com/text2audio?%@",valueStr]]
completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
if(error != nil){
NSLog(@"发生异常了");
}
}] resume];
//将这段字符串返回
NSString *urlStr = [NSString stringWithFormat:@"https://tsn.baidu.com/text2audio?%@",valueStr];
return urlStr;
}
}
return nil;
}
///URL编码的字符串
- (NSString *)URLEncodedString:(NSString *)str{
NSString *unencodedString = str;
NSString *encodedString = [unencodedString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
return encodedString;
}
///URL解码字符串
- (NSString *)URLDecodedString:(NSString *)str{
NSString *encodedString = str;
NSString *decodedString = [encodedString stringByRemovingPercentEncoding];
return decodedString;
}
@end
网友评论