在移動互聯(lián)網(wǎng)時代,幾乎所有的應(yīng)用都用到網(wǎng)絡(luò)請求易迹,只有通過網(wǎng)絡(luò)和外界進(jìn)行數(shù)據(jù)交互宰衙、數(shù)據(jù)更新,應(yīng)用才能保持新鮮和活力睹欲。網(wǎng)絡(luò)編程也是 iOS 面試中常問到的問題供炼。下面整理一下 iOS 開發(fā)中涉及到的網(wǎng)絡(luò)編程知識:HTTP、HTTPS窘疮、NSURLConnection 和 NSURLSession
(一)HTTP 協(xié)議和 HTTPS 協(xié)議
HTTP協(xié)議
HTTP(Hypertext Transfer Protocol)協(xié)議是超文本傳輸協(xié)議袋哼,是互聯(lián)網(wǎng)上應(yīng)用最為廣泛的一種網(wǎng)絡(luò)協(xié)議。簡單來說闸衫,HTTP 是客戶端和服務(wù)器端之間請求和應(yīng)答的標(biāo)準(zhǔn)涛贯。
HTTP 協(xié)議工作過程
分為4個步驟:
- 客戶端與服務(wù)器需要建立連接。例如蔚出,單擊某個超鏈接弟翘,瀏覽器和服務(wù)器將建立通信連接。
- 建立連接后骄酗,客戶端發(fā)送一個請求給服務(wù)器稀余,請求方式的格式為:統(tǒng)一資源標(biāo)識符(URL)、協(xié)議版本號酥筝,后邊是MIME信息包括請求修飾符滚躯、客戶端信息和可能的內(nèi)容。
- 服務(wù)器接收到請求后,給予相應(yīng)的響應(yīng)信息掸掏,其格式為一個狀態(tài)行茁影,包括信息的協(xié)議版本號、一個成功或錯誤的代碼丧凤,后面是 MIME 信息包括服務(wù)器信息募闲、實(shí)體信息和可能的內(nèi)容。
- 客戶端接收服務(wù)器所返回的信息通過瀏覽器顯示在用戶的顯示屏上愿待,然后客戶機(jī)與服務(wù)器斷開連接浩螺。
HTTPS協(xié)議
HTTPS(Secure Hypertext Transfer Protocol)安全超文本傳輸協(xié)議它是一個安全通信通道,它基于HTTP開發(fā)仍侥,用于在客戶計算機(jī)和服務(wù)器之間交換信息要出。它使用安全套接字層(SSL)進(jìn)行信息交換,簡單來說它是HTTP的安全版农渊。
HTTPS和HTTP的區(qū)別:
1患蹂、https協(xié)議需要到ca申請證書,一般免費(fèi)證書很少砸紊,需要交費(fèi)传于。
2、http是超文本傳輸協(xié)議醉顽,信息是明文傳輸沼溜,https 則是具有安全性的ssl加密傳輸協(xié)議。
3游添、http和https使用的是完全不同的連接方式,用的端口也不一樣,前者是80,后者是443系草。
4、http的連接很簡單,是無狀態(tài)的否淤。
5悄但、HTTPS協(xié)議是由SSL+HTTP協(xié)議構(gòu)建的可進(jìn)行加密傳輸、身份認(rèn)證的網(wǎng)絡(luò)協(xié)議要比http協(xié)議安全石抡。
App Transport Security(簡稱ATS)特性
iOS9中新增App Transport Security(簡稱ATS)特性, 讓原來請求時候用到的HTTP,全部都轉(zhuǎn)向TLS1.2協(xié)議進(jìn)行傳輸助泽,這意味著所有的HTTP協(xié)議都強(qiáng)制使用了HTTPS協(xié)議進(jìn)行傳輸啰扛。如果我們在iOS9下直接進(jìn)行HTTP請求是會報錯,系統(tǒng)會告訴我們不能直接使用HTTP進(jìn)行請求嗡贺,需要在Info.plist中控制ATS的配置隐解。
(二)NSURLConnection 和 NSURLSession 進(jìn)行網(wǎng)絡(luò)請求
NSURLConnection
NSURLConnection 是 iOS 開發(fā)中最經(jīng)典的網(wǎng)絡(luò)請求方案。雖然在蘋果公司推出 NSURLSession 后已經(jīng)不推薦使用 NSURLConnection 了(NSURLConnection 在 iOS 9 被宣布棄用了)诫睬,但是在一些早先構(gòu)建的項(xiàng)目和框架中可能任使用了 NSURLConnection 技術(shù)煞茫,所以還是有必要了解 NSURLConnection。
NSURLConnection 使用步驟
- 創(chuàng)建一個 NSURL 對象,用于設(shè)置請求路徑续徽。
- 創(chuàng)建一個 NSURLRequest 對象蚓曼,并設(shè)置請求頭、請求體等請求參數(shù)钦扭。
- 創(chuàng)建一個 NSURLResponse 對象用于接收響應(yīng)數(shù)據(jù)纫版,一般用 NSURLResponse 的子類 NSHPPTURLResponse。
- 使用 NSURLConnection 發(fā)送同步或異步請求客情。
- 可以使用 NSURLConnectionDelegate 監(jiān)聽網(wǎng)絡(luò)請求的響應(yīng)。
具體實(shí)現(xiàn)可參考網(wǎng)址 NSURLConnection
NSURLSession
在 iOS 9.0 之后,以前使用的 NSURLConnection 被棄用窑眯,蘋果推薦使用 NSURLSession 來替換NSURLConnection 完成網(wǎng)路請求相關(guān)操作获黔。
NSURLSession 使用步驟
NSURLSession 的使用非常簡單,先根據(jù)會話對象創(chuàng)建一個請求Task仰担,然后執(zhí)行該Task即可籽御。
NSURLSessionTask 本身是一個抽象類,在使用的時候惰匙,通常是根據(jù)具體的需求使用它的幾個子類技掏。關(guān)系如下:
下面關(guān)于 NSURLSession 的 GET 和 POST 的使用直接摘錄于 iOS開發(fā)網(wǎng)絡(luò)篇—發(fā)送GET和POST請求(使用NSURLSession) 。非常感謝项鬼。
NSURLSession GET 請求方法
1)確定請求路徑(一般由公司的后臺開發(fā)人員以接口文檔的方式提供)哑梳,GET請求參數(shù)直接跟在URL后面。
2)創(chuàng)建請求對象(默認(rèn)包含了請求頭和請求方法【GET】)绘盟,此步驟可以省略鸠真。
3)創(chuàng)建會話對象(NSURLSession)。
4)根據(jù)會話對象創(chuàng)建請求任務(wù)(NSURLSessionDataTask)龄毡。
5)執(zhí)行請求 Task吠卷。
6)當(dāng)?shù)玫椒?wù)器返回的響應(yīng)后,解析數(shù)據(jù)(XML 或者 JSON)沦零。
代碼如下:
第一種方法:
-(void)getByNSURLSession1
{
//對請求路徑的說明
//http://120.25.226.186:32812/login?username=520it&pwd=520&type=JSON
//協(xié)議頭+主機(jī)地址+接口名稱+祭隔?+參數(shù)1&參數(shù)2&參數(shù)3
//協(xié)議頭(http://)+主機(jī)地址(120.25.226.186:32812)+接口名稱(login)+?+參數(shù)1(username=520it)&參數(shù)2(pwd=520)&參數(shù)3(type=JSON)
//GET請求路操,直接把請求參數(shù)跟在URL的后面以疾渴?隔開,多個參數(shù)之間以&符號拼接
//1.確定請求路徑
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=520it&pwd=520it&type=JSON"];
//2.創(chuàng)建請求對象
//請求對象內(nèi)部默認(rèn)已經(jīng)包含了請求頭和請求方法(GET)
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//3.獲得會話對象
NSURLSession *session = [NSURLSession sharedSession];
//4.根據(jù)會話對象創(chuàng)建一個Task(發(fā)送請求)
/*
第一個參數(shù):請求對象
第二個參數(shù):completionHandler回調(diào)(請求完成【成功|失敗】的回調(diào))
data:響應(yīng)體信息(期望的數(shù)據(jù))
response:響應(yīng)頭信息屯仗,主要是對服務(wù)器端的描述
error:錯誤信息搞坝,如果請求失敗,則error有值
*/
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error == nil) {
//6.解析服務(wù)器返回的數(shù)據(jù)
//說明:(此處返回的數(shù)據(jù)是JSON格式的魁袜,因此使用NSJSONSerialization進(jìn)行反序列化處理)
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
NSLog(@"%@",dict);
}
}];
//5.執(zhí)行任務(wù)
[dataTask resume];
}
//這是 NSURLSession 發(fā)送GET請求的第一種方法
第二種方法:
-(void)getByNSURLSession2
{
//1.確定請求路徑
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=520it&pwd=520it&type=JSON"];
//2.獲得會話對象
NSURLSession *session = [NSURLSession sharedSession];
//3.根據(jù)會話對象創(chuàng)建一個Task(發(fā)送請求)
/*
第一個參數(shù):請求路徑
第二個參數(shù):completionHandler回調(diào)(請求完成【成功|失敗】的回調(diào))
data:響應(yīng)體信息(期望的數(shù)據(jù))
response:響應(yīng)頭信息桩撮,主要是對服務(wù)器端的描述
error:錯誤信息敦第,如果請求失敗,則error有值
注意:
1)該方法內(nèi)部會自動將請求路徑包裝成一個請求對象店量,該請求對象默認(rèn)包含了請求頭信息和請求方法(GET)
2)如果要發(fā)送的是POST請求芜果,則不能使用該方法
*/
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
//5.解析數(shù)據(jù)
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
NSLog(@"%@",dict);
}];
//4.執(zhí)行任務(wù)
[dataTask resume];
}
//這是 NSURLSession 發(fā)送GET請求的第二種方法
NSURLSession POST請求方法
1)確定請求路徑(一般由公司的后臺開發(fā)人員以接口文檔的方式提供)。
2)創(chuàng)建可變的請求對象(因?yàn)樾枰薷模┑婀穑瞬襟E不可以省略师幕。
3)修改請求方法為POST。
4)設(shè)置請求體诬滩,把參數(shù)轉(zhuǎn)換為二進(jìn)制數(shù)據(jù)并設(shè)置請求體霹粥。
5)創(chuàng)建會話對象(NSURLSession)。
6)根據(jù)會話對象創(chuàng)建請求任務(wù)(NSURLSessionDataTask)疼鸟。
7)執(zhí)行任務(wù) Task后控。
8)當(dāng)?shù)玫椒?wù)器返回的響應(yīng)后,解析數(shù)據(jù)(XML 或者 JSON)空镜。
代碼如下:
-(void)postByNSURLSession
{
//對請求路徑的說明
//http://120.25.226.186:32812/login
//協(xié)議頭+主機(jī)地址+接口名稱
//協(xié)議頭(http://)+主機(jī)地址(120.25.226.186:32812)+接口名稱(login)
//POST請求需要修改請求方法為POST浩淘,并把參數(shù)轉(zhuǎn)換為二進(jìn)制數(shù)據(jù)設(shè)置為請求體
//1.創(chuàng)建會話對象
NSURLSession *session = [NSURLSession sharedSession];
//2.根據(jù)會話對象創(chuàng)建task
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login"];
//3.創(chuàng)建可變的請求對象
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//4.修改請求方法為POST
request.HTTPMethod = @"POST";
//5.設(shè)置請求體
request.HTTPBody = [@"username=520it&pwd=520it&type=JSON" dataUsingEncoding:NSUTF8StringEncoding];
//6.根據(jù)會話對象創(chuàng)建一個Task(發(fā)送請求)
/*
第一個參數(shù):請求對象
第二個參數(shù):completionHandler回調(diào)(請求完成【成功|失敗】的回調(diào))
data:響應(yīng)體信息(期望的數(shù)據(jù))
response:響應(yīng)頭信息,主要是對服務(wù)器端的描述
error:錯誤信息吴攒,如果請求失敗张抄,則error有值
*/
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
//8.解析數(shù)據(jù)
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
NSLog(@"%@",dict);
}];
//7.執(zhí)行任務(wù)
[dataTask resume];
}
// 發(fā)送POST請求的方法
NSURLSession 代理方法
有的時候,我們可能需要監(jiān)聽網(wǎng)絡(luò)請求的過程(如下載文件需監(jiān)聽文件下載進(jìn)度)洼怔,那么就需要用到代理方法署惯。
接下來通過代碼簡單說明NSURLSession中普通網(wǎng)絡(luò)請求會涉及代理方法的使用。
#import "ViewController.h"
@interface ViewController ()<NSURLSessionDataDelegate>
@property (nonatomic, strong) NSMutableData *responseData;
@end
@implementation ViewController
-(NSMutableData *)responseData
{
if (_responseData == nil) {
_responseData = [NSMutableData data];
}
return _responseData;
}
//當(dāng)點(diǎn)擊控制器View的時候會調(diào)用該方法
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self delegateTest];
}
//發(fā)送請求镣隶,代理方法
-(void)delegateTest
{
//1.確定請求路徑
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=520it&pwd=520it&type=JSON"];
//2.創(chuàng)建請求對象
//請求對象內(nèi)部默認(rèn)已經(jīng)包含了請求頭和請求方法(GET)
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//3.獲得會話對象,并設(shè)置代理
/*
第一個參數(shù):會話對象的配置信息defaultSessionConfiguration 表示默認(rèn)配置
第二個參數(shù):誰成為代理极谊,此處為控制器本身即self
第三個參數(shù):隊(duì)列,該隊(duì)列決定代理方法在哪個線程中調(diào)用安岂,可以傳主隊(duì)列|非主隊(duì)列
[NSOperationQueue mainQueue] 主隊(duì)列: 代理方法在主線程中調(diào)用
[[NSOperationQueue alloc]init] 非主隊(duì)列: 代理方法在子線程中調(diào)用
*/
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
//4.根據(jù)會話對象創(chuàng)建一個Task(發(fā)送請求)
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request];
//5.執(zhí)行任務(wù)
[dataTask resume];
}
//1.接收到服務(wù)器響應(yīng)的時候調(diào)用該方法
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
//在該方法中可以得到響應(yīng)頭信息轻猖,即response
NSLog(@"didReceiveResponse--%@",[NSThread currentThread]);
//注意:需要使用completionHandler回調(diào)告訴系統(tǒng)應(yīng)該如何處理服務(wù)器返回的數(shù)據(jù)
//默認(rèn)是取消的
/*
NSURLSessionResponseCancel = 0, 默認(rèn)的處理方式,取消
NSURLSessionResponseAllow = 1, 接收服務(wù)器返回的數(shù)據(jù)
NSURLSessionResponseBecomeDownload = 2,變成一個下載請求
NSURLSessionResponseBecomeStream 變成一個流
*/
completionHandler(NSURLSessionResponseAllow);
}
//2.接收到服務(wù)器返回數(shù)據(jù)的時候會調(diào)用該方法域那,如果數(shù)據(jù)較大那么該方法可能會調(diào)用多次
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
NSLog(@"didReceiveData--%@",[NSThread currentThread]);
//拼接服務(wù)器返回的數(shù)據(jù)
[self.responseData appendData:data];
}
//3.當(dāng)請求完成(成功|失敗)的時候會調(diào)用該方法咙边,如果請求失敗,則error有值
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
NSLog(@"didCompleteWithError--%@",[NSThread currentThread]);
if(error == nil)
{
//解析數(shù)據(jù),JSON解析請參考http://www.cnblogs.com/wendingding/p/3815303.html
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:self.responseData options:kNilOptions error:nil];
NSLog(@"%@",dict);
}
}
@end