筆者開源的前端進階之道已有三年之久,至今也有 17k star,承蒙各位讀者垂愛。在當下部份內容已經略微過期,所以決定提筆翻新內容。html
翻新後的內容會所有集合在「幹爆前端」中,有興趣的讀者能夠前往查看。前端
閱讀前重要提示:git
本文非百科全書,只專爲面試複習準備、查漏補缺、深刻某知識點的引子、瞭解相關面試題等準備。github
筆者一直都是崇尚學會面試題底下涉及到的知識點,而不是刷一大堆面試題,結果變了個題型就不會的那種。因此本文和別的面經不同,旨在提煉面試題底下的經常使用知識點,而不是甩一大堆面試題給各位看官。web
你們也能夠在筆者的網站上閱讀,體驗更佳!面試
姊妹篇:17K star 倉庫,解決 90% 的大廠基礎面試題算法
今天的文章從輸入 URL 開始,和你們聊聊這其中前端工程師須要掌握的網絡相關的內容。瀏覽器
注:輸入 URL 到返回數據過程當中會涉及到不少網絡相關知識,筆者只會介紹前端工程師必須瞭解的那部分,其餘的內容讀者能夠自行學習。緩存
當咱們在瀏覽器中鍵入 URL:www.google.com/ 時,瀏覽器會先去尋找該域名所對應的 IP 地址,畢竟最終通訊咱們仍是得用 IP 才能找到對方的地址,域名只是方便用戶記憶的別名。安全
那麼如何找到域名所對應的 IP 地址呢?接下來讓筆者先來給你們介紹咱們遇到的第一塊內容: DNS。
DNS 的做用就是經過域名查詢到具體的 IP。
由於 IP 存在數字和英文的組合(IPv6),很不利於人類記憶,因此就出現了域名。你能夠把域名當作是某個 IP 的別名,DNS 就是去查詢這個別名的真正名稱是什麼。
當你在瀏覽器中想訪問 www.google.com
時,會經過進行如下操做:
com
這個一級域名的服務器google.com
這個二級域名www.google.com
這個三級域名的地址以上介紹的是 DNS 遞歸查詢,還有種是迭代查詢,區別就是前者是由系統配置的 DNS 服務器作請求,獲得結果後將數據返回給客戶端;後者由客戶端去作請求。
這時可能會有好奇的讀者會問:DNS 在作解析的時候,向這些服務器發送的請求究竟是基於 TCP 仍是 UDP 協議的?
其實在當前的網絡環境中,這兩種協議都有用的。經過 UDP 去進行一些數據量少的請求,這時候能用到 UDP 性能快的優點;對於數據量大且須要保證數據完整有序的時候會選擇用 TCP 去請求,保證數據的正確及完整性。
後續的內容中咱們也會介紹 UDP 及 TCP 協議。
當瀏覽器獲取域名的 IP 地址後,立刻會經過 TCP 協議與服務器創建鏈接。接下來咱們來聊聊 TCP 協議的內容。
TCP 創建鏈接須要進行三次握手:
最開始兩端都爲 CLOSED 狀態。在通訊開始前,雙方都會建立 TCB(一個數據結構,裏面包含了協議須要的不少內容,有興趣的能夠自行了解)。 服務器建立完 TCB 後遍進入 LISTEN 狀態,此時開始等待客戶端發送數據。
第一次握手
客戶端向服務端發送鏈接請求報文段(SYN)。該報文段中包含自身的數據通信初始序號。請求發送後,客戶端便進入 SYN-SENT 狀態,x
表示客戶端的數據通訊初始序號。
第二次握手
服務端收到鏈接請求報文段後,若是贊成鏈接,則會發送一個應答(ACK + SYN),該應答中也會包含自身的數據通信初始序號,發送完成後便進入 SYN-RECEIVED 狀態。
第三次握手
當客戶端收到鏈接贊成的應答後,還要向服務端發送一個確認報文(ACK)。客戶端發完這個報文段後便進入ESTABLISHED 狀態,服務端收到這個應答後也進入 ESTABLISHED 狀態,此時鏈接創建成功。
PS:第三次握手能夠包含數據,經過 TCP 快速打開(TFO)技術。其實只要涉及到握手的協議,均可以使用相似 TFO 的方式,客戶端和服務端存儲相同 cookie,下次握手時發出 cookie 達到減小 RTT(一次請求來回的時間) 的目的。
這裏會有個經典面試題:爲何 TCP 須要三次而不是兩次握手?
由於這是爲了防止失效的鏈接請求報文段被服務端接收,從而產生錯誤。
能夠想象以下場景。客戶端發送了一個鏈接請求 A,可是由於網絡緣由形成了超時,這時 TCP 會啓動超時重傳的機制再次發送一個鏈接請求 B。此時請求順利到達服務端,服務端應答完就創建了請求。若是鏈接請求 A 在兩端關閉後終於抵達了服務端,那麼這時服務端會認爲客戶端又須要創建 TCP 鏈接,從而應答了該請求並進入 ESTABLISHED 狀態。此時客戶端實際上是 CLOSED 狀態,那麼就會致使服務端一直等待,形成資源的浪費。
PS:在創建鏈接中,任意一端掉線,TCP 都會重發 SYN 包,通常會重試五次,在創建鏈接中可能會遇到 SYN FLOOD 攻擊。遇到這種狀況你能夠選擇調低重試次數或者乾脆在不能處理的狀況下拒絕請求。
當創建鏈接並請求完成後,TCP 鏈接並不會立刻斷開。得益於 HTTP 1.1 協議中的 keep-alive 屬性,鏈接能夠短暫的保留一段時間,具體如何斷開得看服務端的設置或者客戶端主動 close 掉。
一旦 close,TCP 協議就會進行四次握手來斷開鏈接。
第一次握手
若客戶端 A 認爲數據發送完成,則它須要向服務端 B 發送鏈接釋放請求。
第二次握手
B 收到鏈接釋放請求後,會告訴應用層要釋放 TCP 連接。而後會發送 ACK 包,並進入 CLOSE_WAIT 狀態,表示 A 到 B 的鏈接已經釋放,不接收 A 發的數據了。可是由於 TCP 鏈接時雙向的,因此 B 仍舊能夠發送數據給 A。
第三次握手
B 若是此時還有沒發完的數據會繼續發送,完畢後會向 A 發送鏈接釋放請求,而後 B 便進入 LAST-ACK 狀態。
PS:經過延遲確認的技術(一般有時間限制,不然對方會誤認爲須要重傳),能夠將服務端的第二次和第三次握手合併,延遲 ACK 包的發送。
第四次握手
A 收到釋放請求後,向 B 發送確認應答,此時 A 進入 TIME-WAIT 狀態。該狀態會持續 2MSL(最大段生存期,指報文段在網絡中生存的時間,超時會被拋棄) 時間,若該時間段內沒有 B 的重發請求的話,就進入 CLOSED 狀態。當 B 收到確認應答後,也便進入 CLOSED 狀態。
爲何 A 要進入 TIME-WAIT 狀態,等待 2MSL 時間後才進入 CLOSED 狀態?
爲了保證 B 能收到 A 的確認應答。若 A 發完確認應答後直接進入 CLOSED 狀態,若是確認應答由於網絡問題一直沒有到達,那麼會形成 B 不能正常關閉。
創建和斷開鏈接涉及到的兩種握手都是 TCP 中很重要的知識。固然除了這些內容以外,TCP 也還有不少東西咱們須要學習,好比說 TCP 協議是如何實現有序且完整的傳遞數據,這其中涉及到的內容咱們立刻就會學到。
首先咱們先來學習 TCP 協議是如何實現完整送達數據的。畢竟網絡波動及各類不肯定因素的存在會致使數據傳遞發生丟失,一旦出現這種狀況咱們得確保協議有重傳的機制。就比如咱們在業務中上傳某些重要數據同樣,一次沒收到響應或者發生別的狀況時,會進行屢次重試。
ARQ 協議也就是超時重傳機制協議。經過確認和超時機制保證了數據的正確送達,其中包含中止等待 ARQ 和連續 ARQ 協議。
正常傳輸過程
只要 A 向 B 發送一段報文,都要中止發送並啓動一個定時器,等待對端迴應,在定時器時間內接收到對端應答就取消定時器併發送下一段報文。
出現錯誤時
1 .報文丟失或出錯
在報文傳輸的過程當中可能會出現丟包。這時候超過定時器設定的時間就會再次發送丟包的數據直到對端響應,因此須要每次都備份發送的數據。
即便報文正常的傳輸到對端,也可能出如今傳輸過程當中報文出錯的問題。這時候對端會拋棄該報文並等待 A 端重傳。
PS:通常定時器設定的時間都會大於一個 RTT 的平均時間。
2. ACK 超時或丟失
對端傳輸的應答也可能出現丟失或超時的狀況。那麼超過定時器時間 A 端照樣會重傳報文。這時候 B 端收到相同序號的報文會丟棄該報文並重傳應答,直到 A 端發送下一個序號的報文。
在超時的狀況下也可能出現應答很遲到達,這時 A 端會判斷該序號是否已經接收過,若是接收過只須要丟棄應答便可。
這個協議的缺點就是傳輸效率低,在良好的網絡環境下每次發送報文都得等待對端的 ACK 。
在連續 ARQ 中,發送端擁有一個發送窗口,能夠在沒有收到應答的狀況下持續發送窗口內的數據,這樣相比中止等待 ARQ 協議來講減小了等待時間,提升了效率。
累計確認
連續 ARQ 中,接收端會持續不斷收到報文。若是和中止等待 ARQ 中接收一個報文就發送一個應答同樣,就太浪費資源了。經過累計確認,能夠在收到多個報文之後統一回復一個應答報文。報文中的 ACK 能夠用來告訴發送端這個序號以前的數據已經所有接收到了,下次請發送這個序號 + 1的數據。
可是累計確認也有一個弊端。在連續接收報文時,可能會遇到接收到序號 5 的報文後,並未接到序號 6 的報文,然而序號 7 之後的報文已經接收。遇到這種狀況時,ACK 只能回覆 6,這樣會形成發送端重複發送數據,這種狀況下能夠經過 Sack 來解決,這個會在下文說到。
窗口這個概念在 TCP 中常常會看到,就好比咱們在上面小節中講到了發送窗口。在 TCP 中,兩端都維護着窗口:分別爲發送端窗口和接收端窗口。
發送端窗口包含已發送但未收到應答的數據和能夠發送可是未發送的數據。
發送端窗口大小是由接收窗口剩餘大小決定的。接收方會把當前接收窗口的剩餘大小寫入應答報文,發送端收到應答後根據該值和當前網絡擁塞狀況設置發送窗口的大小,因此發送窗口的大小是不斷變化的。
當發送端接收到應答報文後,會隨之將窗口進行滑動
刷過算法的同窗看到這兩張圖應該會很熟悉,畢竟滑動窗口在算法中也是一類高頻題目。
滑動窗口實現了流量控制。接收方經過報文告知發送方還能夠發送多少數據,從而保證接收方可以來得及接收數據。
在發送報文的過程當中,可能會遇到對端出現零窗口的狀況。在該狀況下,發送端會中止發送數據,並啓動 persistent timer 。該定時器會定時發送請求給對端,讓對端告知窗口大小。在重試次數超過必定次數後,可能會中斷 TCP 連接。
擁塞處理是 TCP 中做用很大的功能模塊,主要經過一些算法來控制數據的傳輸,防止擁塞網絡。
擁塞處理和流量控制不一樣,後者是做用於接收方,保證接收方來得及接受數據。而前者是做用於網絡,防止過多的數據擁塞網絡,避免出現網絡負載過大的狀況。
擁塞處理包括了四個算法,分別爲:慢開始,擁塞避免,快速重傳,快速恢復。
慢開始算法,顧名思義,就是在傳輸開始時將發送窗口慢慢指數級擴大,從而避免一開始就傳輸大量數據致使網絡擁塞。舉個例子在平常下載時,咱們的下載網速都是逐漸變快的。
慢開始算法步驟具體以下:
擁塞避免算法相比簡單點,每過一個 RTT 窗口大小隻加一,這樣可以避免指數級增加致使網絡擁塞,慢慢將大小調整到最佳值。
在傳輸過程當中若是協議認爲網絡擁塞了,會立刻進行如下步驟:
快速重傳通常和快恢復一塊兒出現。一旦接收端收到的報文出現失序的狀況,接收端只會回覆最後一個順序正確的報文序號(沒有 Sack 的狀況下)。若是收到三個重複的 ACK,說明發送端傳過去的數據對端都沒有收到,此時會啓動快速重傳。主要算法爲:
TCP Reno
TCP New Reno 算法改進了以前 TCP Reno 算法的缺陷。在以前,快恢復中只要收到一個新的 ACK 包,就會退出快恢復。
在 TCP New Reno 中,TCP 發送方先記下三個重複 ACK 的分段的最大序號。
假如我有一個分段數據是 1 ~ 10 這十個序號的報文,其中丟失了序號爲 3 和 7 的報文,那麼該分段的最大序號就是 10。發送端只會收到 ACK 序號爲 3 的應答。這時候重發序號爲 3 的報文,接收方順利接收並會發送 ACK 序號爲 7 的應答。這時候 TCP 知道對端是有多個包未收到,會繼續發送序號爲 7 的報文,接收方順利接收並會發送 ACK 序號爲 11 的應答,這時發送端認爲這個分段接收端已經順利接收,接下來會退出快恢復階段。
說完了 TCP 協議,咱們再來聊聊它的兄弟協議 UDP 吧。由於 UDP 相對 TCP 功能簡單,所以涉及到的知識並很少。
UDP 是一個面向報文(報文能夠理解爲一段段的數據)的協議。意思就是 UDP 只是報文的搬運工,不會對報文進行任何拆分和拼接操做。TCP 協議就會這樣幹,畢竟得保證報文的有序。
具體來講:
因爲 UDP 對於報文處理的很簡單,所以會帶來一些弊端。
由於 UDP 沒有 TCP 那麼複雜,須要保證數據不丟失且有序到達。因此 UDP 的頭部開銷小,只有八字節,相比 TCP 的至少二十字節要少得多,在傳輸數據報文時是很高效的。
頭部包含了如下幾個數據
講完 TCP 及 UDP 這兩塊內容之後,咱們接下去看瀏覽器對於 www.google.com/ 連接在進行完 TCP 三次握手後的後續動做是什麼。
由於咱們輸入的協議是 HTTPS,這個協議咱們能夠當作是 HTTP 協議加上 TLS 安全協議。所以在進行完 TCP 鏈接後不會立刻開始 HTTP 協議層面的傳輸,而是開始 TLS 協議的握手。
HTTPS 最重要的組成部分就是 TLS 協議了,由於是這個協議保證了安全性。
既然須要保證安全性,那麼確定須要用到加密技術。在 TLS 中使用了兩種加密技術,分別爲:對稱加密和非對稱加密。
對稱加密:
對稱加密就是兩邊擁有相同的祕鑰,兩邊都知道如何將密文加密解密。
非對稱加密:
有公鑰私鑰之分,公鑰全部人均可以知道,能夠將數據用公鑰加密,可是將數據解密必須使用私鑰解密,私鑰只有分發公鑰的一方纔知道。
TLS 握手過程以下圖:
經過以上步驟可知,在 TLS 握手階段,兩端使用非對稱加密的方式來通訊,可是由於非對稱加密損耗的性能比對稱加密大,因此在正式傳輸數據時,兩端使用對稱加密的方式通訊。
PS:以上說明的都是 TLS 1.2 協議的握手狀況,在 1.3 協議中,首次創建鏈接只須要一個 RTT,後面恢復鏈接不須要 RTT 了。所以若是在乎網絡傳輸性能的話,應該選用 TLS1.3 協議。
當 TLS 完成握手之後,就真的開始進行 HTTP 協議層面的傳輸數據了。
對於 HTTP 協議來講,前端工程師主要了解常見狀態碼以及 header 便可,畢竟這些是咱們平常編碼中常常須要接觸的內容。
2XX 成功
3XX 重定向
4XX 客戶端錯誤
5XX 服務器錯誤
通用字段 | 做用 |
---|---|
Cache-Control | 控制緩存的行爲 |
Connection | 瀏覽器想要優先使用的鏈接類型,好比 keep-alive 、close |
Date | 建立報文時間 |
Pragma | 報文指令 |
Via | 代理服務器相關信息 |
Transfer-Encoding | 傳輸編碼方式 |
Upgrade | 要求客戶端升級協議 |
Warning | 在內容中可能存在錯誤 |
請求字段 | 做用 |
---|---|
Accept | 能正確接收的媒體類型 |
Accept-Charset | 能正確接收的字符集 |
Accept-Encoding | 能正確接收的編碼格式列表 |
Accept-Language | 能正確接收的語言列表 |
Expect | 期待服務端的指定行爲 |
From | 請求方郵箱地址 |
Host | 服務器的域名 |
If-Match | 兩端資源標記比較 |
If-Modified-Since | 本地資源未修改返回 304(比較時間) |
If-None-Match | 本地資源未修改返回 304(比較標記) |
User-Agent | 客戶端信息 |
Max-Forwards | 限制可被代理及網關轉發的次數 |
Proxy-Authorization | 向代理服務器發送驗證信息 |
Range | 請求某個內容的一部分 |
Referer | 表示瀏覽器所訪問的前一個頁面 |
TE | 傳輸編碼方式 |
響應字段 | 做用 |
---|---|
Accept-Ranges | 是否支持某些種類的範圍 |
Age | 資源在代理緩存中存在的時間 |
ETag | 資源標識 |
Location | 客戶端重定向到某個 URL |
Proxy-Authenticate | 向代理服務器發送驗證信息 |
Server | 服務器名字 |
WWW-Authenticate | 獲取資源須要的驗證信息 |
實體字段 | 做用 |
---|---|
Allow | 資源的正確請求方式 |
Content-Encoding | 內容的編碼格式 |
Content-Language | 內容使用的語言 |
Content-Length | request body 長度 |
Content-Location | 返回數據的備用地址 |
Content-MD5 | Base64加密格式的內容 MD5檢驗值 |
Content-Range | 內容的位置範圍 |
Content-Type | 內容的媒體類型 |
Expires | 內容的過時時間 |
Last_modified | 內容的最後修改時間 |
最後幾段內容咱們再來聊聊一些新的協議,先來聊聊 HTTP 2.0。這個協議相比於 HTTP 1.1 而言,能夠說是大幅度提升了 web 的性能。
在 HTTP 1.1 中,爲了性能考慮,咱們會引入雪碧圖、將小圖內聯、使用域名發散等等的方式。這一切都是由於瀏覽器限制了同一個域名下的請求數量,當頁面中須要請求不少資源的時候,隊頭阻塞(Head of line blocking)會致使在達到最大請求數量時,剩餘的資源須要等待其餘資源請求完成後才能發起請求。
可是在 HTTP 2.0 中這個問題被極大地優化了,可是仍是沒有被解決。由於 HTTP 2.0 底下仍是 TCP 協議,TCP 須要保證數據正確性的作法也會帶來隊頭阻塞的問題,因此說問題並沒被解決。可是這個問題被後續的新協議完全解決了,咱們下文再表。
咱們先來感覺下 HTTP 2.0 比 HTTP 1.X 到底快了多少,能夠經過 該連接 體驗。
在 HTTP 1.X 中,由於隊頭阻塞的緣由,你會發現請求是這樣的
在 HTTP 2.0 中,由於引入了多路複用,你會發現請求是這樣的:
在 HTTP 2.0 中,有兩個很是重要的概念,分別是幀(frame)和流(stream)。
幀表明着最小的數據單位,每一個幀會標識出該幀屬於哪一個流,流也就是多個幀組成的數據流。
多路複用,就是在一個 TCP 鏈接中能夠存在多條流。換句話說,也就是能夠發送多個請求,對端能夠經過幀中的標識知道屬於哪一個請求。經過這個技術能夠極大的提升傳輸性能。
HTTP 2.0 中全部增強性能的核心點在於此。在以前的 HTTP 版本中,咱們是經過文本的方式傳輸數據。在 HTTP 2.0 中引入了新的編碼機制,全部傳輸的數據都會被分割,並採用二進制格式編碼爲二進制幀。
二進制幀分爲不少類型,在上圖中咱們能夠發現存在了 HEADERS 幀和 DATA 幀,除了這些以外還有還幾種,各位讀者有興趣的話能夠自行了解。
在 HTTP 1.X 中,咱們使用文本的形式傳輸 header,在 header 攜帶 cookie 的狀況下,可能每次都須要重複傳輸幾百到幾千的字節。
在 HTTP 2.0 中,使用了 HPACK 壓縮格式對傳輸的 header 進行編碼,減小了 header 的大小。並在兩端維護了索引表,用於記錄出現過的 header ,後面在傳輸過程當中就能夠傳輸已經記錄過的 header 的鍵名,對端收到數據後就能夠經過鍵名找到對應的值。
這個不用學了,由於用的太少,Chrome 或移除這個功能了。詳情見 Chrome to remove HTTP/2 Push。
這是一個谷歌出品的基於 UDP 實現的同爲傳輸層的協議,目標很遠大,但願替代 TCP 協議。
網絡協議涉及到的內容其實還有至關多,筆者這裏聊了聊前端工程師必需要學習的內容。若是你們對某一個協議感興趣的話能夠自行學習。
舉幾個例子:
拋一些學習引子給各位讀者。對於上文中的內容若是你們有疑惑的話能夠一塊兒交流學習。
你們也能夠在筆者的網站上閱讀,體驗更佳!