文章目錄
- 前言
- 最原始的網路下載 -- NSData + NSURL方式
- NSURLConnection 和 NSURLSession
- GET請求
- 下載完的事件采用block形式
- 下載完的事件采用delegate形式
- POST請求
- GET和POST操作的區別
- 使用情況
- 使用POST方法
- 使用GET方法
- HTTP與HTTPS
- NSURLSessionConfiguration
- 創建方式
- 檔案下載
- 斷點續傳
- NSURLSessionTaskTransactionMetrics
前言
最近計算機網路該學習網路層的內容,想著自己把iOS中網路請求的部分好好復習一下
以及看面經時的斷點續傳自己一直不知道怎么實作,所以統一來了解一下
iOS開發中的網路下載方式包括NSData(最原始,實際開發基本不會用),NSURLConnection(古老又過氣的蘋果原生網路框架),NSURLSession(現在流行的蘋果網路框架),AFNetworking,SDWebImage以及基于AFNetworking的二次封裝框架例如XMNetworking,HYBNetworking等等
最原始的網路下載 – NSData + NSURL方式
- 步驟:NSString -> NSURL -> NSData -> UIImage
- 關鍵API
URLWithString dataWithContentsOfURL:url imageWithData:data - 示例:
// 在子執行緒中發送下載檔案請求
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 創建下載路徑
NSURL *url = [NSURL URLWithString:@"......"];
// NSData的dataWithContentsOfURL:方法下載
NSData *data = [NSData dataWithContentsOfURL:url];
// 回到主執行緒,重繪UI
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = [UIImage imageWithData:data];
});
});
NSURLConnection 和 NSURLSession
- iOS9.0之后,以前使用的NSURLConnection過期,蘋果推薦使用NSURLSession來替換NSURLConnection來完成網路請求相關操作
- NSURLSession的優勢:
- NSURLSession支持http2.0協議
- 支持下載任務的時候可以直接把資料下載到磁盤中
- 支持后臺下載和上傳
- 同一個session發送多次請求,只需要建立一次連接(復用了TCP)
- 提供了全域的session并且可以統一配置,使用更加方便
- 下載的時候時多執行緒異步處理,效率更高
- NSURLSessionTask及其子類
NSURLSessionTask本身是一個抽象類,在使用時,通常是根據具體的需求使用它的幾個子類:NSURLSessionDataTask可以用來發送常見的Get,Post請求,既可以用來上傳也可以用來下載NSURLSessionDownloadTask可以用來發送下載請求,專門用來下載資料NSURLSessionUploadTask可以用來發送上傳請求,專門用來上傳資料

GET請求
程序如下:
- 確定請求路徑(一般由公司的后臺發開人員以介面檔案的方式提供),GET請求引數直接跟在URL后面
- 創建請求物件(默認包含了請求頭和請求方法【GET】)
- 創建會話物件(NSURLSession)
- 根據會話物件創建請求任務(NSURLSessionDataTask)
- 執行Task
- 得到服務器回傳的回應后,決議資料
下載完的事件采用block形式
- 第一個API,通過request來提供引數(當然requese是基于URL的)
//1. 確定請求路徑
NSURL *url = [NSURL URLWithString:@"http://news-at.zhihu.com/api/4/news/latest"];
//2. 創建請求物件
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//3. 創建會話物件
NSURLSession *session = [NSURLSession sharedSession];
//4. 根據會話物件創建請求任務
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error == nil) {
//6.決議服務器回傳的資料
//說明:(此處回傳的資料是JSON格式的,因此使用NSJSONSerialization進行反序列化處理)
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
NSLog(@"%@",dict);
}
}];
[task resume];
- 第二個API,直接通過URL來提供引數
dataTaskWithURL:completionHandler:
這兩個API就使用上來說無非就是第一個使用request將URL進行了封裝,但是要注意直接通過URL來提供請求物件的方法在POST請求種不能使用,
下載完的事件采用delegate形式
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
//1. 確定請求路徑
NSURL *url = [NSURL URLWithString:@"http://news-at.zhihu.com/api/4/news/latest"];
//2. 創建請求物件
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//3. 創建會話物件
// 第一個引數:會話物件的配置資訊defaultSessionConfiguration 表示默認配置
// 第二個引數:誰成為代理,此處為控制器本身即self
// 第三個引數:佇列,該佇列決定代理方法在哪個執行緒中呼叫
// 可以傳主佇列、非主佇列 如果不指定執行緒,則completionHandler和delegate的回呼方法,都會在子執行緒中執行,
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
//4. 根據會話物件創建請求任務
NSURLSessionDataTask *task = [session dataTaskWithRequest:request];
[task resume];
}
//1.接收到服務器回應的時候呼叫該方法
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {
NSLog(@"%@",response);
completionHandler(NSURLSessionResponseAllow);
//注意:需要使用completionHandler回呼告訴系統應該如何處理服務器回傳的資料
//默認是取消的
/*
NSURLSessionResponseCancel = 0, 默認的處理方式,取消
NSURLSessionResponseAllow = 1, 接收服務器回傳的資料
NSURLSessionResponseBecomeDownload = 2,變成一個下載請求
NSURLSessionResponseBecomeStream 變成一個流
*/
}
//2.接收到服務器回傳資料的時候會呼叫該方法,如果資料較大那么該方法可能會呼叫多次
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
NSLog(@"%@",data);
}
//3.當請求完成(成功|失敗)的時候會呼叫該方法,如果請求失敗,則error有值
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
NSLog(@"%@",error);
}
POST請求
簡單來說 POST請求需要另外單獨設定request.HTTPMethod屬性
//1.創建會話物件
NSURLSession *session = [NSURLSession sharedSession];
//2.根據會話物件創建task
NSURL *url = [NSURL URLWithString:@"http://116.62.21.180:8088/user/get_detail"];
//3.創建可變的請求物件
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//4.修改請求方法為POST
request.HTTPMethod = @"POST";
//5.設定請求體
request.HTTPBody = [@"phone=xxxxxxxxxxxx" dataUsingEncoding:NSUTF8StringEncoding];
//6.根據會話物件創建一個Task(發送請求)
/*
第一個引數:請求物件
第二個引數:completionHandler回呼(請求完成【成功|失敗】的回呼)
data:回應體資訊(期望的資料)
response:回應頭資訊,主要是對服務器端的描述
error:錯誤資訊,如果請求失敗,則error有值
*/
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (!error) {
//8.決議資料
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
NSLog(@"%@",dict);
} else {
NSLog(@"%@",error);
}
}];
//7.執行任務
[dataTask resume];

GET和POST操作的區別
區別:
- GET向服務器獲取資料,POST向服務器發送請求
- GET會把查詢字串的引數追加到URL的末尾,POST請求則把資料作為請求的主體來提交,可以包含非常多的資料,因此客戶可以看到GET提交的引數,POST則不可以,
- GET請求提交的資料直接加到URL的末尾,所以大小有限制,而POST則沒有
- POST安全性比GET高
- 對于get方式,服務器端有Request.QueryString來獲取變數的值,對于post方式,服務器用Request.Form來獲取提交的資料
- get形式的URL對搜索引擎更加友好,可以提高搜索引擎排名,而POST甚至會阻止爬蟲和搜索引擎的訪問
使用情況
使用POST方法
- 請求的結果有持續性的影響改變,比如向資料庫內添加新的資料行
- 表單收集的資料過多,若使用get方式會使URL過長
- 要傳送的資料不是采用7位的ASCII編碼
使用GET方法
- 請求是為了查找資源,表單的資料只是用來幫助搜索
- 請求結果無持續性的影響改變
- 收集的資料及HTML表單內的輸入欄位名稱的總長不超過1024個位元組
HTTP與HTTPS
在使用時,如果不設定Info檔案,會出現某些問題:

那么HTTP與HTTPS有什么區別呢
HTTP協議運行在TCP之上,明文運輸,客戶端與服務器端都無法驗證對方的身份;HTTPS是身披SSL(Secure Socket Layer)外殼的Http,運行于SSL上,SSL運行在TCP上,是添加了加密和認證機制的HTTP,Https的加密機制是一種共享密鑰加密和公開密鑰加密并用的混合加密機制,
- 埠不同:Http和Https使用不同的連接方式,http是80,https是443
- 資源消耗:和Http通信相比,Https通信會由于加減密處理消耗更多的CPU和記憶體資源
- 開銷:Https通信需要證書,而證書一般需要向認證機構購買
NSURLSessionConfiguration
NSURLSession創建的Task任務,只能在任務結束的completionHandler的block中獲取到結束后的資料,想要使用這些資料的話也需要等到下載完成了,才能拿來使用,至于下載的程序中,想要使用資料不可能,而NSURlSessionConfiguration就是一個代理,是為了監控下載程序的,
所以除了上面的兩種session的創建方式sharedSession、sessionWithConfiguration:delegate:delegateQueue:,還有sessionWithConfiguration:可以幫助我們監控下載程序,
創建方式
NSURLSessionConfiguration可以設定請求的Cookie、密鑰、快取、請求頭等引數,將網路請求的一些配置引數從NSURLSession中分離出來,
NSURLSessionConfiguration提供defaultSessionConfiguration的方式創建,但這并不是單例方法,而是類方法,創建的是不同物件,通過這種方式創建的configuration,并不會共享cookie、cache、密鑰等,而是不同configuration都需要單獨設定,
檔案下載
這里URL為http://vfx.mtime.cn/Video/2017/03/31/mp4/170331093811717750.mp4一個在線的視頻流
很簡單,通過Session創建一個downloadTask,并呼叫resume即可開啟一個下載任務
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:config
delegate:self
delegateQueue:[NSOperationQueue mainQueue]];
NSURL *url = [NSURL URLWithString:@"http://vfx.mtime.cn/Video/2017/03/31/mp4/170331093811717750.mp4"];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request];
[downloadTask resume];
// 從服務器接收資料,下載進度回呼
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
}
// 下載完成后回呼
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location {
}
當然我們也可以呼叫suspend將下載任務掛起,隨后呼叫resume方法繼續下載任務,suspend和resume需要是成對的,但是suspend掛起任務是由超市的,默認是60s,如果超時系統會將TCP連接斷開,我們再呼叫resume是失效的,可以通過NSURLSessionConfiguration的timeoutIntervalForResource來設定上傳和下載的資源耗時,suspend只針對于下載任務,其他任務掛起后將會重新開始,
斷點續傳
斷點續傳就是 斷點續傳就是從檔案賞賜中斷的地方重新開始下載或者上傳資料,而不是從頭檔案開始,

HTTP協議支持斷點續傳操作,在開始下載請求時通過請求頭設定Range欄位,標示從什么位置開始下載,
Range:bytes=512000-
服務端收到客戶端請求后,開始從512kb的位置開始傳輸資料,并通過Content-Range欄位告知客戶端傳輸資料的開始位置
Content-Range:bytes 512000-/1024000
downloadTask任務開始請求后,可以呼叫cancelByProducingResumeData:方法可以取消下載,并且可以獲得一個resumeData,resumeData中存放一些斷點下載的資訊,可以將resumedata寫到本地,后面通過這個檔案可以進行斷點續傳,
NSString *library = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES).firstObject;
NSString *resumePath = [library stringByAppendingPathComponent:[self.downloadURL md5String]];//以URL為值,創建一個path
[self.downloadTask cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
[resumeData writeToFile:resumePath atomically:YES];//將得到的resumeData傳入url的路徑中
}];
在創建下載任務前,可以判斷當前任務有沒有之前待恢復的任務,如果有的話呼叫downloadTaskWithResumeData:方法并傳入一個resumeData,可以恢復之前的下載,并重新創建一個downloadTask任務,
NSString *library = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES).firstObject;
NSString *resumePath = [library stringByAppendingPathComponent:[self.downloadURL md5String]];
NSData *resumeData = [[NSData alloc] initWithContentsOfFile:resumePath];
self.downloadTask = [self.session downloadTaskWithResumeData:resumeData];
[self.downloadTask resume];
通過suspend和resume這種方式掛起的任務,downloadTask是同一個物件,而通過cancel然后resumeData恢復的任務,會創建一個新的downloadTask任務,
當呼叫downloadTaskWithResumeData方法恢復下載之后,會產生回呼下面的方法,
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes;
fileOffset是上次檔案的下載大小
expectedTotalBytes是預估的檔案總大小
通過這兩個引數,我們也能給出某些比例之類的,比如下載了多少等等
NSURLSessionTaskTransactionMetrics
NSURLSessionTaskTransactionMetrics中的屬性都是用來做統計的,功能都是記錄某個值,并沒有邏輯上的意義,

// 請求物件
@property (copy, readonly) NSURLRequest *request;
// 回應物件,請求失敗可能會為nil
@property (nullable, copy, readonly) NSURLResponse *response;
// 請求開始時間
@property (nullable, copy, readonly) NSDate *fetchStartDate;
// DNS決議開始時間
@property (nullable, copy, readonly) NSDate *domainLookupStartDate;
// DNS決議結束時間,如果決議失敗可能為nil
@property (nullable, copy, readonly) NSDate *domainLookupEndDate;
// 開始建立TCP連接時間
@property (nullable, copy, readonly) NSDate *connectStartDate;
// 結束建立TCP連接時間
@property (nullable, copy, readonly) NSDate *connectEndDate;
// 開始TLS握手時間
@property (nullable, copy, readonly) NSDate *secureConnectionStartDate;
// 結束TLS握手時間
@property (nullable, copy, readonly) NSDate *secureConnectionEndDate;
// 開始傳輸請求資料時間
@property (nullable, copy, readonly) NSDate *requestStartDate;
// 結束傳輸請求資料時間
@property (nullable, copy, readonly) NSDate *requestEndDate;
// 接收到服務端回應資料時間
@property (nullable, copy, readonly) NSDate *responseStartDate;
// 服務端回應資料傳輸完成時間
@property (nullable, copy, readonly) NSDate *responseEndDate;
// 網路協議,例如http/1.1
@property (nullable, copy, readonly) NSString *networkProtocolName;
// 請求是否使用代理
@property (assign, readonly, getter=isProxyConnection) BOOL proxyConnection;
// 是否復用已有連接
@property (assign, readonly, getter=isReusedConnection) BOOL reusedConnection;
// 資源識別符號,表示請求是從Cache、Push、Network哪種型別加載的
@property (assign, readonly) NSURLSessionTaskMetricsResourceFetchType resourceFetchType;
// 本地IP
@property (nullable, copy, readonly) NSString *localAddress;
// 本地埠號
@property (nullable, copy, readonly) NSNumber *localPort;
// 遠端IP
@property (nullable, copy, readonly) NSString *remoteAddress;
// 遠端埠號
@property (nullable, copy, readonly) NSNumber *remotePort;
// TLS協議版本,如果是http則是0x0000
@property (nullable, copy, readonly) NSNumber *negotiatedTLSProtocolVersion;
// 是否使用蜂窩資料
@property (readonly, getter=isCellular) BOOL cellular;
這個怎么呼叫啊 一堆屬性,一個init方法,一個new方法,怎么才能查看具體的TCp連接時間呢?
不知道 也沒搜到 那說這有啥用啊…
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/301788.html
標籤:其他
上一篇:顯示兩張圖片
