
Alamofire初探一. Alamofire概述二. URLSesstion基礎三、TCP的三次握手四、TCP數據的傳輸過程五、TCP的四次揮手一. Alamofire概述對于使用Objective-C的開發者一定非常熟悉AFNetworking這個網絡框架。在蘋果推出的Swift之后AFNetworking的作者專門用Swift來編寫一個類似AFNetworking的網絡框架稱為Alamofire。Alamofire地址因為Alamofire是對蘋果URLSesstion的封裝,所以先來了解下URLSesstion的基礎二. URLSesstion基礎URLSession.shared.dataTask(with: url) { (data, response, error) in if error nil { print(請求成功\(String(describing: response)) ) } }.resume()此過程省略了一個重要的東西URLSessionConfigurationopen class var default: URLSessionConfiguration { get } open class var ephemeral: URLSessionConfiguration { get } available(iOS 8.0, *) open class func background(withIdentifier identifier: String) - URLSessionConfigurationURLSessionConfiguration有三種模式default默認模式通常使用這種模式就夠了default模式下系統會創建一個持久化的緩存并在用戶的鑰匙串中存儲證書ephemeral:系統沒有任何持久性存儲所有內容的生命周期與session相同當session無效時所有內容自動釋放let configuration1 URLSessionConfiguration.default let configuration2 URLSessionConfiguration.ephemeral print(沙盒大小: \(String(describing: configuration1.urlCache?.diskCapacity))) print(內存大小: \(String(describing: configuration1.urlCache?.memoryCapacity))) print(沙盒大小: \(String(describing: configuration2.urlCache?.diskCapacity))) print(內存大小: \(String(describing: configuration2.urlCache?.memoryCapacity)))background創建一個可以在后臺甚至app已經關閉的時候仍然在傳輸數據的會話。background模式可以在程序掛起退出崩潰的情況下運行task.也可以利用標識符來進行恢復。注意后臺session一定要在創建的時候賦予一個唯一的identifier,這樣在app下次運行的時候能夠根據identifier來進行相關的區分如果用戶關閉了app,ios系統會關閉所有的background Session.而且被用戶強制關閉了以后iOS系統不回主動喚醒app,只有用戶下次啟動了app,數據傳輸才會繼續let configuration URLSessionConfiguration.background(withIdentifier: self.createID()) let session URLSession.init(configuration: configuration, delegate: self, delegateQueue: OperationQueue.main) session.downloadTask(with: url).resume()session代理extension ViewController:URLSessionDownloadDelegate{ func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { // 下載完成 - 開始沙盒遷移 print(下載完成 - \(location)) let locationPath location.path //拷貝到用戶目錄文件名以時間戳命名 let documnets NSHomeDirectory() /Documents/ self.lgCurrentDataTurnString() .mp4 print(移動地址:\(documnets)) //創建文件管理器 let fileManager FileManager.default try! fileManager.moveItem(atPath: locationPath, toPath: documnets) } func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) { print( bytesWritten \(bytesWritten)\n totalBytesWritten \(totalBytesWritten)\n totalBytesExpectedToWrite \(totalBytesExpectedToWrite)) print(下載進度: \(Double(totalBytesWritten)/Double(totalBytesExpectedToWrite))\n) } }注意上面的設置還是不能達到后臺下載還需要設置下面2步開啟后臺下載權限 (The completion handler to call when you finish processing the events. Calling this completion handler lets the system know that your app’s user interface is updated and a new snapshot can be taken.)用于保存后臺下載的completionHandler var backgroundSessionCompletionHandler: (() - Void)? func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: escaping () - Void) { self.backgroundSessionCompletionHandler completionHandler }回調系統回調告訴系統及時更新屏幕func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) { print(后臺任務下載回來) DispatchQueue.main.async { guard let appDelegate UIApplication.shared.delegate as? AppDelegate, let backgroundHandle appDelegate.backgroundSessionCompletionHandler else { return } backgroundHandle() } }三、TCP的三次握手http請求是基于tcp連接的tcp有6種表示位SYN(synchronous建立聯機)ACK(acknowledgement確認)PSH(push傳送)FIN(finish結束)RST(reset重置)URG(urgent緊急)sequence number(順序號碼)acknowledge number(確認號碼)客戶端向服務器發出連接請求報文這時報文首部中的同部位SYN1,同時隨機生成初始序列號seqx,此時TCP客戶端進程進入了SYN-SENT(同步已發送狀態)狀態。TCP規定SYN報文段(SYN1的報文段)不能攜帶數據但需要消耗掉一個序號。這個三次握手中的開始表示客戶端想要和服務端建立連接。TCP服務器收到請求報文后如果同意連接則發出確認報文確認報文中應該ACK1,SYN1,確認號是ackx1,同時也要自己隨機初始化一個序列號seqy,此時TCP服務器進程進入了SYN-RCVD(同步收到)狀態這個報文也不能攜帶數據但是同樣要消耗一個序號。這個報文帶有SYN(建立連接)和ACK(確認)標志詢問客戶端是否準備好。TCP客戶進程收到確認后還要向服務器給出確認。確認報文的ACK1,acky1,此時TCP連接建立客戶端進入ESTABLISHED(已建立連接)狀態TCP規定ACK報文段可以攜帶數據但是如果不攜帶數據則不消耗序號這里客戶端表示我已經準備好。為什么要三次握手呢舉例已失效的連接請求報文段客戶端發送了第一個連接的請求報文但由于網絡不好這個請求沒有立即到達服務端而是在某個網絡節點中滯留了直到某個時間才到達server本來這已經是一個失效的報文但是server端收到這個請求報文后還是會像客戶端發送確認的報文,表示同意連接。假如不采用三次握手那么server發出確認后新的建立就連接了但其實這個請求是失效的請求客戶端是不會理睬server端的確認信息的也不會像服務端發送確認的請求,但是server認為新的連接已經建立起來了并一直等待客戶端發來的數據這樣server端很多資源都白白浪費掉了采用三次握手就是為了防止這種情況的發生server會因為收不到確認的報文就知道客戶端并沒有建立連接這就是三次握手的作用。四、TCP數據的傳輸過程建立連接后兩臺主機就可以相互傳輸數據了。如下圖所示主機A初始seq為1200,滑動窗體為100,向主機B傳遞數據的過程。假設主機B在完全成功接收數據的基礎上,那么主機B為了確認這一點向主機A發送 ACK 包并將 Ack 號設置為 1301。因此按如下的公式確認 Ack 號Ack號 Seq號 傳遞的字節數 1 這是在完全接受成功的情況下主機A獲得B傳來的ack(1301)后,開始發送seq為1301,滑動窗體為100的數據。…與三次握手協議相同最后加 1 是為了告訴對方要傳遞的 Seq 號。上面說了主機B完全成功接收A發來的數據才是這樣的,如果存在丟包該如何下面分析傳輸過程中數據包丟失的情況如下圖所示上圖表示通過 Seq 1301 數據包向主機B傳遞100字節的數據但中間發生了錯誤主機B未收到。經過一段時間后主機A仍未收到對于 Seq 1301 的ACK確認因此嘗試重傳數據。為了完成數據包的重傳TCP套接字每次發送數據包時都會啟動定時器如果在一定時間內沒有收到目標機器傳回的 ACK 包那么定時器超時數據包會重傳。五、TCP的四次揮手TCP發送一個FIN(結束)用來關閉客戶端到服務端的連接客戶端進程發出連接釋放報文并且停止發送數據。釋放數據報文首部FIN1,其序列號為sequ(等于前面已經傳送過來的數據的最后一個字節的序號1)此時客戶端進入FIN-WAIT-1(終止等待1)的狀態。TCP規定FIN報文段即使不攜帶數據也要消耗一個序號。服務端收到這個FIN他發回一個ACK(確認)確認收到序號為收到序號1和SYN一樣一個FIN將占用一個序號。服務器收到連接釋放報文發出確認報文ACK1acku1并且帶上自己的序列號seqv此時服務端就進入了CLOSE-WAIT關閉等待狀態。TCP服務器通知高層的應用進程客戶端向服務器的方向就釋放了這時候處于半關閉狀態即客戶端已經沒有數據要發送了但是服務器若發送數據客戶端依然要接受。這個狀態還要持續一段時間也就是整個CLOSE-WAIT狀態持續的時間。客戶端收到服務器的確認請求后此時客戶端就進入FIN-WAIT-2終止等待2狀態等待服務器發送連接釋放報文在這之前還需要接受服務器發送的最后的數據。服務端發送一個FIN(結束)到客戶端服務端關閉客戶端的連接。服務器將最后的數據發送完畢后就向客戶端發送連接釋放報文FIN1acku1由于在半關閉狀態服務器很可能又發送了一些數據假定此時的序列號為seqw此時服務器就進入了LAST-ACK最后確認狀態等待客戶端的確認。客戶端發送ACK(確認)報文確認并將確認的序號1這樣關閉完成。客戶端收到服務器的連接釋放報文后必須發出確認ACK1ackw1而自己的序列號是sequ1此時客戶端就進入了TIME-WAIT時間等待狀態。注意此時TCP連接還沒有釋放必須經過2??MSL最長報文段壽命的時間后當客戶端撤銷相應的TCB后才進入CLOSED狀態。服務器只要收到了客戶端發出的確認立即進入CLOSED狀態。同樣撤銷TCB后就結束了這次的TCP連接。可以看到服務器結束TCP連接的時間要比客戶端早一些。為什么是4次揮手呢為了確保數據能夠完成傳輸。關閉連接時當收到對方的FIN報文通知時它僅僅表示對方沒有數據發送給你了但未必你所有的數據都全部發送給對方了所以你可以未必會馬上會關閉SOCKET,也即你可能還需要發送一些數據給對方之后再發送FIN報文給對方來表示你同意現在可以關閉連接了所以它這里的ACK報文和FIN報文多數情況下都是分開發送的。可能有人會有疑問tcp我握手的時候為何ACK(確認)和SYN(建立連接)是一起發送。揮手的時候為什么是分開的時候發送呢.因為當Server端收到Client端的SYN連接請求報文后可以直接發送SYNACK報文。其中ACK報文是用來應答的SYN報文是用來同步的。但是關閉連接時當Server端收到FIN報文時很可能并不會立即關閉 SOCKET所以只能先回復一個ACK報文告訴Client端“你發的FIN報文我收到了”。只有等到我Server端所有的報文都發送完了我才能發送FIN報文因此不能一起發送。故需要四步握手。客戶端突然掛掉了怎么辦正常連接時客戶端突然掛掉了如果沒有措施處理這種情況那么就會出現客戶端和服務器端出現長時期的空閑。解決辦法是在服務器端設置保活計時器每當服務器收到客戶端的消息就將計時器復位。超時時間通常設置為2小時。若服務器超過2小時沒收到客戶的信息他就發送探測報文段。若發送了10個探測報文段每一個相隔75秒還沒有響應就認為客戶端出了故障因而終止該連接。