iOS老司機的網絡相關Tips
持續創作,加速成長!這是我參與「掘金日新計劃 · 10 月更文挑戰」的第3天,點擊查看活動詳情
1. 前言: 作為一個有自我修養的iOS工程師, 對網絡的理解體現在開發中的方方面面
- 如果你是個科班畢業的iOS開發者, 對計算機網絡的基礎認識可能是通過<<計算機網絡>>這本教材.
- 如果你是個其他專業轉向iOS的開發者, 對計算機網絡的認識可能來自於這本無數大佬安利的<<圖解HTTP>>
- 下面就由我們來結合iOS開發日常工作中的方方面, 來一起對iOS中常用的網絡相關知識做一個不成體系的梳理, 拋磚引玉, 如在閲讀過程中發現錯誤, 請及時在評論區交流指正:)
2. iOS開發中的網絡相關基操Tips
2.1 HTTP的請求方式有哪些?
- GET、POST、PUT、DELETE、HEAD、OPTIONS
- 其實我們在開發過程中用的最多的請求方式的是GET和POST, 對其他的⽅式使⽤的很少, 在有些公司採⽤restful⻛格的時候會⽤PUT來做更新, DELETE做刪除.
2.1.1 GET和POST的區別
- 從用法來説, GET主要是用來獲取資源的, 語義上具有安全性、冪等性、可緩存性;
- 從用法來説, POST主要是用來修改資源的, 語義上具有不安全性、非冪等性、不可緩存性;
-
- 安全性, 指的是是否能引起server端的變化.
-
- 冪等性, 指的是單次請求和多次請求的結果是否一致.
-
- 可緩存性, 指的是請求是否可以被緩存.
2.2 關於HTTP的持久連接的理解
- 隨着我們要處理的資源越來越多, 如果還是採⽤非持久連接的⽅式就會很浪費資源, 因為會牽扯到⼤量的連接的建⽴和銷燬.
- 所以為了解決這種問題, 提出了持久連接來改進.
- 就是在連接建⽴以後, 處理完任務不是⽴即銷燬, ⽽是有⼀個等待時間, 在這段時間範圍內可以對同⼀服務的請求進⾏復⽤, 減少連接的創建和銷燬次數.
2.3 怎樣判斷一個HTTP請求是否已經完成了?
- 判斷⼀個請求是否完成的⽅式有兩種.
- 第⼀種: 通過響應報⽂中的
content-length
字段來判斷接收的數據是否已經接收完畢了, 如果接收完 畢了就表示本次請求完成了. - 第⼆種: 在POST請求中會存在多次響應, 在最後⼀次響應的報⽂中會包含⼀個空的
chunck
, 我們可以以此來判斷請求是否完成了.
2.4 HTTP連接過程中的三次握手和四次揮手
2.4.1 三次握手
- 發送⼀個http請求要先建⽴連接.
- ⾸先server端要監聽端⼝, 此時server端處於LISTEN狀態.
- client端發送⼀個SYN同步包到server端, 並將⾃⼰(client端)的狀態置為SYN_SENT
- server端收到client的syn, 會回傳⼀個ACK確認信息, 以及⼀個SYN同步包, 同意建⽴連接, 將server 端狀態置為SYN_RCVD.
- client端收到服務端的SYN包後, 發送⼀個ACK給server端. 並將⾃⼰的狀態改為ESTABLISHED.
- server端接收到ACK信息後, 會將狀態改為ESTABLISHED.
- 這樣就完成了三次握⼿, 就可以進⾏數據的傳遞了.
2.4.2 四次揮手
- 關於斷開連接的四次揮⼿的流程, 主要是因為全雙工通道的機制.
- client端發送⼀個FIN信號給server端, 並進⼊FIN_WAIT_1狀態.
- server端接收到消息後會返回⼀個ACK確認, 並進⼊CLOSE_WAIT狀態.
- client端接收到ACK確認信息後, 將狀態改為FIN_WAIT_2. 此時已經完成了半關閉狀態.
- 當server端要傳輸的數據也完成以後, 就會發送⼀個FIN信號給client端, 並將狀態置為LAST_ACK.
- client端接收到FIN信號後會返回⼀個ACK給server端, 並將狀態置為TIME_WAIT.
- server端在收到ACK確認信息後, 會進⼊CLOSE狀態.
- client端會在等待2MSL(報⽂最⼤⽣存時間)的時間後進⼊CLOSE狀態.
2.4.3 為什麼要等待2MSL?
- 為了保證可靠關閉.
- 如果server沒有收到最後⼀個ACK, 那麼就會重發FIN.
- 為了避免端⼝重⽤帶來的數據混淆, 如果client直接進⼊CLOSE狀態, ⼜⽤相同的端⼝建⽴⼀個連接, 上⼀次連接的部分數據在⽹絡中延遲到達server, 數據就有可能發⽣混淆.
2.5 TCP和UDP的區別
- TCP是傳輸控制協議, 它的特點是: ⾯向連接的、可靠的、⾯向字節流的, 流量控制、擁塞控制.
- UDP是⽤户數據報協議, 它的特點是: ⽆連接、盡最⼤可能交付、⾯向報⽂的, 不保證數據的完整 性.
2.5.1 TCP是怎樣保證傳輸過程的可靠性的?
- TCP在保證傳輸的可靠性⽅⾯主要有以下機制
- 校驗和: 發送⽅在發送數據之前計算校驗和, 接收⽅收到數據後同樣計算, 如果不⼀致, 那麼傳輸有 誤.
- 確認應答: TCP進⾏傳輸時數據都進⾏編號, 每次接收⽅返回ACK都有確認序列號.
- 超時重傳: 如果發送⽅發送數據⼀段時間後沒有收到ACK, 那麼就重發數據.
- 對於超時重傳的情況有兩種
-
- ⼀種是客户端發送到服務端的數據超時了, 那麼服務端可能會收到兩份數據, 服務端會丟棄重複的數據.
-
- 還有⼀種是服務端確認信息超時了, 那麼客户端在收到確認信息的時候什麼都不做.
2.5.2 TCP的流量控制與擁塞控制
- TCP的流量控制是通過滑動窗⼝協議來實現的. TCP協議報⽂頭包含16位的窗⼝⼤⼩, 接收⽅在返回ACK時會把⾃⼰的即時窗⼝填⼊, 發送⽅就根據報⽂中窗⼝的⼤⼩來控制發送的速度.
- 擁塞控制是為了解決⽹絡中湧⼊⼤量數據包的⼀種機制.
- 這⾥主要使⽤慢開始, 擁塞避免和快恢復, 快重傳兩種策略.
- - 慢開始是指剛開始發送數據的時候, 擁塞窗⼝是1, 以後每次收到ACK後, 將擁塞窗⼝的值按照指數級增⻓, 當增⻓達到了擁塞窗⼝的⻔限值的時候, 就會以線性⽅式增⻓, 來控制發送數據的速度.
- - 當發送⽹絡擁塞的時候, 就會將擁塞窗⼝值改為1, 重新開始慢開始的流程. 同時將擁塞窗⼝的⻔限值改為擁塞時的⼀半.
- - 快恢復快重傳是基於慢開始的, 在發⽣⽹絡擁塞的時候, 是把擁塞窗⼝的⼤⼩調到擁塞值的⼀半, 然後以線性⽅式增⻓.
2.6 DNS域名解析相關
- DNS是做域名解析的, 我們在通過域名訪問服務器的時候, ⾸先會通過DNS進⾏域名解析, 得到服務器的IP地址, 然後再進⾏訪問.
- 域名解析使⽤的UDP的⽅式進⾏明⽂傳輸的. 採⽤udp是因為它的速度更快.
- 但是因為傳輸的明⽂, 所以容易遭到劫持.
- 所以在解決DNS劫持的時候主要有兩種⽅式, ⼀種是httpDNS, ⼀種是⻓連接.
- - httpDNS是指通過發送⼀個http請求來解析域名. 因為普通的DNS劫持是基於UDP的, 所以我們通過 httpDNS是採⽤TCP的⽅式, 可以防⽌.
- - ⻓連接的⽅式是指我們建⽴⼀個⻓連接服務器, 讓client與中間服務器建⽴⻓連接, 並將⻓連接與域名解析服務器通過內⽹專線的⽅式進⾏連接, 以此來避免DNS的劫持.
2.7 如何保證Cookie的安全
- 對cookie內容進⾏加密處理, 這種⽅式的缺點是不能避免腳本攻擊.
- 在https下傳輸cookie, 這也是我們使⽤最多的⽅式.
- 設置cookie為httpOnly來防⽌跨站腳本攻擊.
2.8 關於HTTPS
- HTTPS是在HTTP的基礎上增加了安全模塊(TLS/SSL).
2.8.1 HTTPS的連接建立流程
1. ⾸先client端將當前⽀持的TLS版本號, 以及⽀持的加密算法, 並⽣成⼀個隨機數C⼀起發送給server端.
2. server端收到消息後會選擇⼀套加密算法, 並⽣成⼀個隨機數S, 連同server證書返回給client端
3. client端先校驗server證書, 然後根據預主祕鑰+C+S組裝會話祕鑰. 並通過server證書將預主祕鑰加密發送給server端.
4. server端⽤私鑰進⾏解碼, 得到預主祕鑰後, 組裝會話祕鑰, 然後加密握⼿消息.
2.9 ISO模型的七層架構(應表會傳網數物)
- 七層架構分別是: 應⽤層、表示層、會話層、傳輸層、⽹絡層、數據鏈路層、物理層.
- 應⽤層: 最⾼層, 直接⾯對⽤户.
- 表示層: ⽤來將數據轉換成另外⼀種格式, ⽐如⽂字、視頻、圖⽚等.
- 會話層: 負責建⽴和斷開連接.
- 傳輸層: TCP、UDP都屬於這⼀層的協議.
- ⽹絡層: 定義了IP和⼦⽹掩碼, ⽤來確定⽹段, 通過路由器和交換機進⾏傳輸. IP協議就是屬於⽹絡層的協議.
- 數據鏈路層: 把⽐特流封裝成數據幀的格式.
- 物理層: 通過⽹線, 光纜等這種物理⽅式將電腦連接起來, 傳遞的是⽐特流.
2.10 什麼是BIO/NIO/AIO?
- BIO: 同步阻塞IO, 每⼀個客户端連接, 服務端都會對應⼀個處理線程, 對於沒有分配到處理線程的連 接就會被阻塞或者拒絕. 相當於⼀個連接⼀個線程.
- NIO: 同步⾮阻塞IO, 基於Reactor模型, 客户端和channel進⾏通信, channel可以進⾏讀寫操作. 通過多路復⽤器seletor來輪詢註冊在其上的channel, ⽽後再進⾏IO操作. 這樣的話, 在進⾏IO操作的時候再⽤⼀個線程去處理就好. 也就是⼀個請求⼀個線程.
- AIO: 異步⾮阻塞IO, 相⽐NIO更進⼀步, 完全有操作系統來完成請求的處理, 然後通知服務端開啟線 程進⾏處理, 因此是⼀個有效請求⼀個線程.
2.11 淺談一下對HTTP的理解
- ⾸先http是超⽂本傳輸協議, 它是基於TCP協議的應⽤層傳輸協議.
- 它的特點主要有兩個, ⽆連接和⽆狀態
- - 關於⽆連接是指限制每次連接只處理⼀個請求. 服務器處理完客户端的請求, 並收到客户端的應答後 就斷開連接. 這是早期採⽤的⽅式, 為了追求快, 傳輸的資源少. 但是隨着我們要處理的資源增多, 就提出了持久連接的⽅式, 來複⽤連接, 通過設置⼀個時間, 在這段時間內都可以復⽤此次連接, 只有在超過這個時間後才會斷開連接.
- - 關於⽆狀態是指每次請求不會記錄狀態, 為了解決這種⽆狀態的問題, 提出了cookie和session機制. cookie和session都是⽤來記錄⽤户狀態, 區分⽤户的. 主要的區別在於cookie是保存在客户端, session是存儲在服務端的. session的實現也是基於cookie機制的.
- 另外我們在發送⼀個http請求的時候還主要請求報⽂和響應報⽂.
- - 關於請求報⽂主要包含請求⾏, 在請求⾏⾥包含了請求⽅式是GET還是POST, 還有URL, 以及當前協議的版本是http1.0還是1.1.
- - 除了請求⾏還有請求的頭部字段, 這⾥是以key-value的形式存在, 包含多個鍵值對
- - 還有就是實體主體, 在get請求時沒有這⼀塊, 在POST請求時有.
- - 關於響應報⽂也包含響應⾏、頭部字段、以及實體主體. 在響應報⽂的響應⾏中存在着當前的版本、狀態碼、短語, 也就是描述信息.
2.12 常見的狀態碼
- 1xx: 目前是協議的中間狀態, 還需要後續請求
-
- 100: 請求者應當繼續發送請求, 服務端返回100表示已收到請求的第一部分, 正在等待剩餘部分.
-
- 101: 切換請求協議, 客户端已要求服務端切換協議, 服務端已確認並準備切換. 如從HTTP切換到WebSocket
- 2xx: 表示請求成功
-
- 200: 請求成功, 有響應體, 服務器成功返回數據
-
- 204: 服務端成功處理了請求, 但沒有返回任何內容(無內容)
-
- 205: 服務端成功處理了請求, 但沒有返回任何內容(重置內容)
- 3xx: 表示重定向狀態, 需要重新請求
-
- 301: 永久重定向, 會緩存
-
- 302: 臨時重定向, 不會緩存
-
- 304: 協商緩存命中
- 4xx: 表示客户端請求報文錯誤
-
- 400: 請求錯誤, 服務端不理解請求的語法
-
- 403: 服務器禁止訪問, 拒絕請求
-
- 404: 資源未找到, 請求的網頁不存在
- 5xx: 表示服務端錯誤
-
- 500: 服務器端錯誤, 無法完成請求
-
- 503: 服務器繁忙(服務不可用, 由於超載或停機維護, 通常只是暫時狀態)
-
- 504: 服務端作為網關或代理, 但是沒有及時從上游服務器收到請求.(網關超時)
發文不易, 喜歡點讚的人更有好運氣👍 :), 定期更新+關注不迷路~
ps:歡迎加入筆者18年建立的研究iOS審核及前沿技術的三千人扣羣:662339934,坑位有限,備註“掘金網友”可被羣管通過~
- iOS老司機聊聊實際項目開發中的<<人月神話>>
- iOS老司機可落地在中大型iOS項目中的5大接地氣設計模式合集
- iOS老司機的跨端跨平台Hybrid開發Tips
- iOS老司機的2022年回顧, 聊聊寒冬下的實用<<談判力>>
- iOS老司機可落地的中大型iOS項目中的設計模式優化Tips_橋接模式
- iOS老司機的多線程PThread學習分享
- iOS老司機整理, iOSer必會的經典算法_2
- iOS老司機的<<藍海轉型>>讀書分享
- iOS老司機的<<程序員的自我修養:鏈接、裝載與庫>>讀書分享
- iOS老司機的接地氣算法Tips
- iOS老司機的RunLoop原理探究及實用Tips
- iOS老司機整理, iOSer必會的經典算法_1
- iOS老司機的App啟動優化Tips, 讓啟動速度提升10%
- iOS老司機的網絡相關Tips
- 戀上數據結構與算法
- iOS老司機帶你一起把App的崩潰率降到0.1%以下
- 探究Swift的String底層實現
- iOS老司機萬字整理, 可能是最全的Swift Tips
- iOS老司機可落地的中大型iOS項目中的設計模式優化Tips
- 聊一聊Swift中的閉包