此文章為引用,原文鏈接:http://www.reibang.com/p/2501a618ad1b
一牛郑、首先聊一聊這些年蘋果爸爸對網(wǎng)絡層的改變
1淹朋、在2003年隨著第一版Safari的發(fā)布就發(fā)布了NSURLConnection。 作為網(wǎng)絡基礎架構(gòu)杈抢,這些年也服務了成千上萬的iOS和Mac OS程序惶楼,也做得相當?shù)牟诲e鲫懒。
2刽辙、2013年蘋果推出iOS 7宰缤,這個版本的iOS被稱為革命性的iOS,而我們以前所用到的NSURLConnection這個網(wǎng)絡庫也被蘋果爸爸給重構(gòu)朦乏, 并且重新命名為NSURLSession呻疹。
3镊尺、2014年的iOS 8蘋果爸爸宣布正式淘汰NSURLConnection庐氮,同時發(fā)布了App Extensions。
4输涕、2015年iOS 9蘋果爸爸讓網(wǎng)絡變得更加安全了莱坎。
(1)App Transport Security (ATS):核心目的是防止意外泄露用戶的敏感數(shù)據(jù)型奥,ATS加強了NSURLSession的默認策略螟深,現(xiàn)在的NSURLSession不允許明文的HTTP加載烫葬,他只會使用HTTPS連接搭综。
(2)NSURLSession支持HTTP/2協(xié)議:如果已經(jīng)在程序中使用NSURLSession兑巾,所有的這些都會自動轉(zhuǎn)換帅掘。HTTP/2協(xié)議已經(jīng)無縫集成到NSURLSession的API中,不需要修改任何代碼工程就可支持HTTP/2協(xié)議。
(3)watchOS 支持NSURLSession:之前iWatch通過藍牙和iphone連接吱窝,iphone下載完成在通過藍牙傳到watch≡合浚現(xiàn)在watch可以通過使用NSURLSession自己下載。
(4)共享cookies:iOS8推出了App Extension实抡,之前App與其Extension都默認對數(shù)據(jù)的持有進行獨立處理,就像放在兩個不同的數(shù)據(jù)集合里,而現(xiàn)在可以讓App和其Extension共享同一個數(shù)據(jù)集合。具體代碼如下:
(5)增加NSURLSessionStreamTask:以前我們使用NSInputStream/NSOutputStream來進行一些非HTTP的連接, 例如利用TCP連接一臺遠程的服務器等等, 現(xiàn)在我們有了NSURLSessionStreamTask讓我們更簡單地實現(xiàn)以上功能。
6场靴、2016年 iOS 10新增NSURLSessionTaskMetrics和NSURLSessionTaskTransactionMetrics:對發(fā)送請求/DNS查詢/TLS握手/請求響應等各種環(huán)節(jié)時間上的統(tǒng)計。更易于我們檢測魄咕, 分析我們的請求緩慢到底是發(fā)生在哪個環(huán)節(jié),并對此進行優(yōu)化提升我們APP的性能奠蹬。
7嗡午、2017年iOS 11蘋果爸爸在網(wǎng)絡層做了新增和優(yōu)化了。
(1)提供了NSProgressReporting協(xié)議言蛇,且NSURLSessionTask實現(xiàn)了這個協(xié)議,讓我們能夠獲得progress對象满哪,這個progress對象可以以0~1.0的方式告訴你當前進度,而不用你自己去拿到已獲得的數(shù)據(jù)量去除以需要獲得的數(shù)據(jù)總量從而得出進度活鹰。然后這個progress對象跟NSURLSessionTask的綁定是雙向的:你調(diào)用progress對象的cancel、pause、resume也會使得task變?yōu)閏ancel夸楣、pause、resume讲衫,反之亦然。
(2)新增waitsForConnectivity屬性枷畏,以前進行網(wǎng)絡調(diào)用時触趴,如果網(wǎng)絡不通冗懦,那么系統(tǒng)就會報個錯告訴你網(wǎng)絡不通。這時候你要么輪詢要么讓用戶手動retry没讲,然后網(wǎng)絡通了請求才能發(fā)送出去±梗現(xiàn)在只需要把NSURLSessionConfiguration的waitsForConnectivity設置成YES迁霎,這樣如果請求發(fā)送的時候網(wǎng)絡不通,那么這個請求就會等到網(wǎng)絡通了的時候再發(fā)出去。
(3)在NSURLSessionTask的delegate里面新增了一個方法urlSession:task:willBeginDelayedRequest:completionHandler:系統(tǒng)在發(fā)起請求之前會調(diào)一個這個回調(diào)啄刹,然后在這個completionHandler里面你告訴系統(tǒng)這個請求是否要發(fā)出去,是否要修改捷雕。(因為創(chuàng)建的后臺請求在還沒發(fā)出去的時候可能因為上下文變化的原因?qū)е逻@個請求無意義)
(4)earliestBeginDate屬性,在后臺請求的時候以前系統(tǒng)不知道什么時候去發(fā)起你的請求才是最合適的 久橙,現(xiàn)在給task設置一個earliestBeginDate,系統(tǒng)在這之前是不會發(fā)起請求的祝拯。
(5)iOS 11可以通過設置NSURLSessionTask的countOfBytesClientExpectsToSend和countOfBytesClientExpectsToReceive來讓系統(tǒng)更好地調(diào)度你的后臺網(wǎng)絡任務。
(6)multipathServiceType:移動設備多路協(xié)議,這使得移動設備的TCP包可以在這兩個(多個)鏈路上隨意切換著發(fā)(同時開啟兩個流量鏈路)亭珍,而不必斷線重連。
(7)iOS 11支持ECN(顯式擁塞通知)可以最大化的使用網(wǎng)絡帶寬,減少包的重發(fā)次數(shù)粱侣,降低延遲。還有iOS 11里的網(wǎng)絡操作被移動到User Space去了。iOS11支持Brotli壓縮算法。iOS11更新了Public Suffix List该肴。
8秦效、未來會有哪些變化
(1)TLS1.3:蘋果要把網(wǎng)絡庫整體遷移到支持TLS1.3阱州,年底TLS1.3的標準應該能出來×⒀疲現(xiàn)在基于TLS1.3草稿的實現(xiàn)可以弄下來自己測試著玩了铛绰。最新的TLS1.3草稿已經(jīng)出到21了:draft-ietf-tls-tls13-21。
(2)QUIC:Google搞了個QUIC晦譬,蘋果在跟進敛腌。QUIC可以理解成UDP實現(xiàn)的TCP+TLS+HTTP/2集合體尤莺。主要是提高了數(shù)據(jù)傳輸效率和鏈接效率。目前QUIC的開發(fā)才剛剛開始,項目網(wǎng)站提供了玩具客戶端和玩具服務端給大家玩:Playing with QUIC柔纵。
二系羞、聊一聊NSURLSession
1、支持data, ftp, http(s)協(xié)議, 同時支持代理服務器和socks網(wǎng)關.
2杠人、支持http/1.1, http/2, spdy協(xié)議, 但同時需要服務器支持ALPN和NPN.
3学歧、ALPN(Application Layer Protocol Negotiation袁铐,應用層協(xié)議協(xié)商)
4、NPN(Next Protocol Negotiation徙融,下一代協(xié)議協(xié)商)
5、NPN是服務端發(fā)送它支持的HTTP協(xié)議列表饺饭, 供客戶端選擇; 而ALPN則相反,由客戶端發(fā)送它支持的HTTP協(xié)議列表, 供服務端選擇。如果缺少NPN/ALPN其中一個, 則無法使用HTTP/2通信胸哥。
想詳細了解HTTP/2的可以參考這兩篇文章為什么我們應該盡快支持 ALPN和談談 HTTP/2 的協(xié)議協(xié)商機制
6庐船、NSURLSession相關類為 :
? ? ?? NSURLSession
? ? ?? NSURLSessionConfiguration
? ? ?? NSURLSessionDelegate
? ? ?? NSURLSessionTask
? ? ?? NSURLSessionTaskMetrics
? ? ?? NSURLSessionTaskTransactionMetrics
用一個簡單的圖來表示一下他們各個類之間的關系:
(一)同名類NSURLSession
初始化方式:
? 1赋朦、 [NSURLSession sharedSession];全局單例session壹将,有一定的局限性。
? 2、自定義配置文件辛臊,設置代理
?? [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:nil];
? ? [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
3、后臺session(由NSURLSessionConfiguration)配置负溪。
(二)NSURLSessionConfiguration
NSURLSessionConfiguration 有三個類方法川抡,這很好地說明了 NSURLSession 設計時所考慮的不同的使用場景。
1楔脯、+defaultSessionConfiguration 返回一個標準的 configuration堪嫂,共享 NSHTTPCookieStorage,共享 NSURLCache 和共享 NSURLCredentialStorage恶复。
2姥宝、+ephemeralSessionConfiguration 返回一個預設配置断序,這個配置中不會對緩存,Cookie 和證書進行持久性的存儲诸迟。這對于實現(xiàn)像秘密瀏覽這種功能來說是很理想的。
3、+backgroundSessionConfiguration:(NSString *)identifier 創(chuàng)建一個后臺 session快耿。后臺 session 不同于常規(guī)的,普通的 session,它甚至可以在應用程序掛起丁稀,退出或者崩潰的情況下運行上傳和下載任務线衫。
NSURLSessionConfiguration擁有20多個配置屬性:
1、基本配置
(1)HTTPAdditionalHeaders 指定了一組默認的可以設置出站請求的數(shù)據(jù)頭白热。這對于跨 session 共享信息,如內(nèi)容類型,語言刨啸,用戶代理和身份認證,是很有用的。
(2)networkServiceType 對標準的網(wǎng)絡流量,網(wǎng)絡電話洒扎,語音磷醋,視頻煌恢,以及由一個后臺進程使用的流量進行了區(qū)分瑰抵。
(3)allowsCellularAccess 和 discretionary 被用于節(jié)省通過蜂窩網(wǎng)絡連接的帶寬。對于后臺傳輸?shù)那闆r肴颊,推薦大家使用 discretionary 這個屬性,因為allowsCellularAccess會把 WiFi 和電源的可用性考慮在內(nèi)。
(4)timeoutIntervalForRequest 和 timeoutIntervalForResource 分別指定了對于請求和資源的超時間隔袜硫。
(5)HTTPMaximumConnectionsPerHost可以在需要時限制連接到特定主機的數(shù)量。
(6)sessionSendsLaunchEvents 指定該 session 是否應該從后臺啟動秽澳。
(7)connectionProxyDictionary 指定了 session 連接中的代理服務器始花。
(8)waitsForConnectivity如果請求發(fā)送的時候網(wǎng)絡不通酷宵,那么這個請求就會等到網(wǎng)絡通了的時候再發(fā)出去。
(9)multipathServiceType多路多協(xié)議網(wǎng)絡操作這使得移動設備的TCP包可以在這兩個(多個)鏈路上隨意切換著發(fā)(同時開啟兩個流量鏈路)朴摊,而不必斷線重連口锭。效果就是:Wi-Fi和Cellular可以共存,相互輔助。
(10)NSURLSessionMultipathServiceTypeHandover(可靠模式)
(11)這種模式下優(yōu)先考慮的是鏈接的可靠性臭胜。只有在Wi-Fi信號不好的時候,流量才會走Cellular。如果Wi-Fi信號好积锅,但是Wi-Fi很慢,這時候也不會切到Cellular鏈路箫爷。
(12)NSURLSessionMultipathServiceTypeInteractive(低延時模式)
(13)這種模式下優(yōu)先考慮的是鏈接的低延時衩婚。系統(tǒng)會看Wi-Fi快還是Cellular快柄慰。如果Cellular比Wi-Fi快,哪怕此時Wi-Fi信號很好,系統(tǒng)也會把流量切到Cellular鏈路凳忙。
(14)NSURLSessionMultipathServiceTypeAggregate(混合模式)
(15)在這種模式下,Wi-Fi和Cellular會同時起作用柳恐。如果Wi-Fi是1G帶寬,Cellular也是1G帶寬,那么你的設備就能享受2G帶寬戈锻。(不能用于生產(chǎn)環(huán)境)
(16)需要注意的是,Multipath Protocols for Mobile Devices這個功能同時也需要服務端支持MPTCP(Multipath TCP)才行,如果服務端不支持的話坪它,光客戶端支持沒用。linux起了一個項目在做這個事情懒震,項目地址:https://multipath-tcp.org。有興趣的同學可以自己去看一下递宅。
2、Cookie策略
HTTPCookieStorage存儲了session所使用的cookie俐填。兩種初始化方式
(1)[NSHTTPCookieStorage sharedHTTPCookieStorage];
(2)[NSHTTPCookieStorage sharedCookieStorageForGroupContainerIdentifier:@"identifier"];iOS 9新增。
(3)NSHTTPCookieAcceptPolicy決定了什么情況下 session 應該接受從服務器發(fā)出的 cookie。
3、安全策略
(1)URLCredentialStorage存儲了NSURLSession所使用的證書额获,默認使用+sharedCredentialStorage單例對象。
(2)TLSMinimumSupportedProtocol和TLSMaximumSupportedProtocol確定session是否支持SSL協(xié)議。
4奥喻、緩存策略
(1)URLCache是 session 使用的緩存憎兽。默認情況下會使用 NSURLCache 的 +sharedURLCache 這個單例對象。
(2)requestCachePolicy 指定了一個請求的緩存響應應該在什么時候返回。
5结耀、自定義協(xié)議
(1)protocolClasses 用來配置特定某個 session 所使用的自定義協(xié)議(該協(xié)議是 NSURLProtocol 的子類)的數(shù)組鳖眼。
(三)NSURLSessionDelegate
session管理的一組tasks共享一個代理, 不想實現(xiàn)代理方法時, 代理傳nil即可。代理協(xié)議分為 :
(1)NSURLSessionDelegate : session-level的代理方法愿卒。
(2)NSURLSessionTaskDelegate : task-level面向all的代理方法。
(3)NSURLSessionDataDelegate : task-level面向data和upload的代理方法。
(4)NSURLSessionDownloadDelegate : task-level的面向download的代理方法。
(5)NSURLSessionStreamDelegate : task-level的面向stream的代理方法辅柴。
(四)NSURLSessionTask
當一個 NSURLSessionDataTask 完成時筏餐,它會帶有相關聯(lián)的數(shù)據(jù)魁瞪,一般來說,服務端對于一個上傳任務的響應也會有相關數(shù)據(jù)返回辅髓,所以 NSURLSessionUploadTask 繼承自 NSURLSessionDataTask。
task 是由一個 NSURLSession 創(chuàng)建的。每個 task 的構(gòu)造方法都對應有或者沒有 completionHandler 這個 block 的兩個版本挺举,例如:有這樣兩個構(gòu)造方法 –dataTaskWithRequest: 和 –dataTaskWithRequest:completionHandler:滤淳。
(1)NSURLSessionTask : Task的抽象基類伤柄。
(2)NSURLSessionDataTask : 以NSData的形式接收一個URLRequest的內(nèi)容。
(3)NSURLSessionUploadTask : 上傳NSData或者本地磁盤中的文件, 完成后以NSData的形式接收一個URLRequest的響應。
(4)NSURLSessionDownloadTask : 下載完成后返回臨時文件在本地磁盤的URL路徑常挚。
(5)NSURLSessionStreamTask : 用于建立一個TCP/IP連接。
以前我們使用NSInputStream/NSOutputStream來進行一些非HTTP的連接, 例如利用TCP連接一臺遠程的服務器等等, 現(xiàn)在我們有了NSURLSessionStreamTask讓我們更簡單地實現(xiàn)以上功能锐秦。
NSURLSessionStreamTask的特性 :
(1)更輕松地使用TCP進行通信。
(2)替代NSInputStream/NSOutputStream, 提供更優(yōu)的API。
(3)異步讀寫API罐寨。
(4)能自動通過HTTP代理, 連接一個遠程服務器衩茸。
(5)輕松轉(zhuǎn)換成NSStream幔烛。
(五)NSURLSessionTaskMetrics 和 NSURLSessionTaskTransactionMetrics
對發(fā)送請求/DNS查詢/TLS握手/請求響應等各種環(huán)節(jié)時間上的統(tǒng)計。更易于我們檢測珠叔,分析我們App的請求緩慢到底是發(fā)生在哪個環(huán)節(jié),并對此進行優(yōu)化提升我們APP的性能。
NSURLSessionTaskMetrics對象與NSURLSessionTask對象一一對應。每個NSURLSessionTaskMetrics對象內(nèi)有3個屬性 :
(1)taskInterval : task從開始到結(jié)束總共用的時間
(2)redirectCount : task重定向的次數(shù)
(3)transactionMetrics : 一個task從發(fā)出請求到收到數(shù)據(jù)過程中派生出的每個子請求, 它是一個裝著許多NSURLSessionTaskTransactionMetrics對象的數(shù)組。每個對象都代表下圖的一個子
API很簡單米间,就一個方法 : - (void)URLSession: task: didFinishCollectingMetrics:择懂, 當收集完成的時候就會調(diào)用該方法。
fetchStart : 開始發(fā)起請求.
domainLookupStart : 發(fā)送DNS請求, 域名->IP地址
domainLookupEnd : DNS請求完成, 拿到IP地址
connectStart : 與遠程服務器開始建立TCP連接
secureConnectionStart : HTTPS的TLS握手開始
secureConnectionEnd : HTTPS的TLS握手完成connectEnd : 與服務器建立起了TCP連接
requestStart : 開始傳輸HTTP header第一個字節(jié)的時間(遠程/緩存)
requestEnd : HTTP最后一個字節(jié)傳輸完成的時間(遠程/緩存)
responseStart : 從服務器得到數(shù)據(jù)(遠程/緩存)
responseEnd : 從服務器接受完最后一個字節(jié)的數(shù)據(jù)(遠程/緩存)
(六)NSSession的整個工作流程
(1)身份驗證或TLS握手:
? ? ? ? 這是所有task都必須經(jīng)歷的一個過程. 當一個服務器請求身份驗證或TLS握手期間需要提供證書的話會調(diào)用這個代理要糊。另外, 如果連接途中收到服務器返回需要身份認證的response, 也會調(diào)用這個代理方法。
(2)重定位response:
? ? ? ? 如果response是HTTP重定位药有, session會調(diào)用下面的代理苇经,這里需要調(diào)用completionHandler告訴session是否允許重定位,或者重定位到另一個URL,或者傳nil表示重定位的響應body有效并返回。如果代理沒有實現(xiàn)該方法稽寒,則允許重定位直到達到最大重定位次數(shù)。
(3)DataTask
? ? ? ? <1> 對于一個data task來說蚓土,session會調(diào)用代理決定是否將一個dataTask轉(zhuǎn)換成download task蜀漆,然后調(diào)用completion回調(diào)繼續(xù)接收data或下載data确丢。
? ? ? ?<2> 在服務器傳輸數(shù)據(jù)給客戶端期間, 代理會周期性地收到
? ? ? ?<3> session會調(diào)用下面代理詢問你的app是否允許緩存。 如果代理不實現(xiàn)這個方法的話描函,默認使用session綁定的Configuration的緩存策略舀寓。
(4)DownloadTask
? ? ? ? ?<1>session先會調(diào)用代理方法。
? ? ? ? <2>在服務器傳輸數(shù)據(jù)給客戶端期間, 調(diào)用下面代理給用戶傳數(shù)據(jù)。
? ? ? ?<3>當用戶暫停下載時齿诞,調(diào)用cancelByProducingResumeData:給用戶傳已下好的數(shù)據(jù)祷杈。
? ? ? ?<4>如果用戶想要恢復下載,把剛剛的resumeData以參數(shù)的形式傳給downloadTaskWithResumeData:方法創(chuàng)建新的task繼續(xù)下載互站。
? ? ? ?<5>如果download task成功完成了,調(diào)用下面代理把臨時文件的URL路徑給你踩叭。此時你應該在該代理方法返回以前讀取他的數(shù)據(jù)或者把文件持久化自脯。
(5)UploadTask
? ? ? ? ?上傳數(shù)據(jù)去服務器期間, 代理會周期性收到URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:回調(diào)并獲得上傳進度的報告膏潮。
(6)StreamTask
? ? ? ? 如果任務的數(shù)據(jù)是由一個stream發(fā)出的焕参, session就會調(diào)用代理的URLSession:task:needNewBodyStream:方法去獲取一個NSInputStream對象并提供一個新請求的body data龟糕。
(7)task completion
? ? ? ? 任何task完成的時候, 都會調(diào)用URLSession:task:didCompleteWithError:方法讲岁,error有可能為nil(請求成功),不為nil(請求失敗)
(8)銷毀session
? ? ? ?如果你不再需要一個session了衬以, 一定要調(diào)用它的invalidateAndCancel或finishTasksAndInvalidate方法來釋放session否則會造成內(nèi)存泄露缓艳。
(七)蘋果爸爸建議的最佳網(wǎng)絡實踐
1、IPv6
蘋果爸爸說:IPv6各種好看峻,大家快來用阶淘。如果你不支持IPv6,爸爸就不讓你上架互妓。要支持IPv6的話溪窒,老老實實用NSURLSession或者CFNetwork就OK了。
不要做的事情:
(1)不用歷史遺留的IPv4 API冯勉。
(2)不要直接用IPv4的地址做鏈接澈蚌,應該用域名去做請求。
(3)發(fā)包前不要做各種檢查:比如你在建立鏈接之前想看一下我當前這個設備是不是IPv4的地址灼狰,這種做法就不行交胚。
(4)不要直接使用socket去發(fā)起請求杯活。
2、不要引入其他的網(wǎng)絡庫均践,要使用蘋果自己的API
? ? ?蘋果并不是在說AFNetworking鞭铆、Alamofire不能用。這些第三方庫本質(zhì)上還是基于NSURLSession舶担,也就是蘋果的API去開發(fā)的闸氮。所以用它們沒問題译断。蘋果的意思是不希望你使用別的基于Socket開發(fā)的網(wǎng)絡庫,例如:ACE该贾、Asio這些理澎。
3、一般來說一個App就一個NSURLSession就夠了
? ? ?以前遷移NSURLConnection到NSURLSession的時候执隧,有人每次都創(chuàng)建新的NSURLSession,但事實上這是沒必要的烁设。各個并行的NSURLSessionTask可以共享同一個NSURLSession。如果你使用了多個NSURLSession的話,記得清理就好疚颊,不清理是會產(chǎn)生內(nèi)存泄漏的。
4母截、NSURLSession的delegate方法和block方法不要同時使用
? ? ?如果你用了block,那么delegate就不會回調(diào)了。這事情僅有兩個特例是兩個都回調(diào)的:taskIsWaitingForConnectivity和didReceiveAuthenticateChallenge盔夜。
三妥泉、了解一下Network Extension Framework
iOS 9 發(fā)布之后蝇率,推出NetworkExtension, 它可給系統(tǒng)WiFi列表列表里邊的WiFi設置密碼 排拷、標簽(副標題) ?來直接點擊連接忙菠。 還可獲取整個WiFi列表,建立VPN等傍睹。
1、iOS 11新增了兩個類:NEHotSpotConfiguration,NEDNSProxyProvider悍抑。
(1)NEHotSpotConfiguration
? ? ? ? NEHotSpotConfiguration可以讓你的智能設備在鏈接手機App之后记靡,能夠很方便地通過在手機App上的操作來實現(xiàn)熱點的鏈接。例如你買了一個網(wǎng)絡攝像頭劫恒,你想要連上攝像頭的Wi-Fi熱點去配置這個攝像頭的話族壳,以前要這么操作:
現(xiàn)在用NEHotSpotConfiguration就能很方便地搞定事情了:
當然,這套API也可以被拿來模擬各種網(wǎng)絡環(huán)境,在測試App的時候很有用舔庶。
(2)NEDNSProxyProvider
NEDNSProxyProvider可以用來設置你的手機如何跟DNS做交互弥鹦。你可以自己發(fā)DNS請求犬钢,也可以自己基于不同的協(xié)議去做DNS查詢。例如DNS over TLS,DNS over HTTP。