UDP
面向報文
UDP 是一個面向報文(報文可以理解爲一段段的數據)的協議。意思就是 UDP 只是報文的搬運工,不會對報文進行任何拆分和拼接操作。
具體來說
- 在發送端,應用層將數據傳遞給傳輸層的 UDP 協議,UDP 只會給數據增加一個 UDP 頭標識下是 UDP 協議,然後就傳遞給網絡層了
- 在接收端,網絡層將數據傳遞給傳輸層,UDP 只去除 IP 報文頭就傳遞給應用層,不會任何拼接操作
不可靠性
- UDP 是無連接的,也就是說通信不需要建立和斷開連接。
- UDP 也是不可靠的。協議收到什麼數據就傳遞什麼數據,並且也不會備份數據,對方能不能收到是不關心的
- UDP 沒有擁塞控制,一直會以恆定的速度發送數據。即使網絡條件不好,也不會對發送速率進行調整。這樣實現的弊端就是在網絡條件不好的情況下可能會導致丟包,但是優點也很明顯,在某些實時性要求高的場景(比如電話會議)就需要使用 UDP 而不是 TCP。
高效
因爲 UDP 沒有 TCP 那麼複雜,需要保證數據不丟失且有序到達。所以 UDP 的頭部開銷小,只有八字節,相比 TCP 的至少二十字節要少得多,在傳輸數據報文時是很高效的。
頭部包含了以下幾個數據
- 兩個十六位的端口號,分別爲源端口(可選字段)和目標端口
- 整個數據報文的長度
- 整個數據報文的檢驗和(IPv4 可選 字段),該字段用於發現頭部信息和數據中的錯誤
傳輸方式
UDP 不止支持一對一的傳輸方式,同樣支持一對多,多對多,多對一的方式,也就是說 UDP 提供了單播,多播,廣播的功能。
TCP
頭部
TCP 頭部比 UDP 頭部複雜的多
對於 TCP 頭部來說,以下幾個字段是很重要的
- Sequence number,這個序號保證了 TCP 傳輸的報文都是有序的,對端可以通過序號順序的拼接報文
- Acknowledgement Number,這個序號表示數據接收端期望接收的下一個字節的編號是多少,同時也表示上一個序號的數據已經收到
- Window Size,窗口大小,表示還能接收多少字節的數據,用於流量控制
- 標識符
- URG=1:該字段爲一表示本數據報的數據部分包含緊急信息,是一個高優先級數據報文,此時緊急指針有效。緊急數據一定位於當前數據包數據部分的最前面,緊急指針標明瞭緊急數據的尾部。
- ACK=1:該字段爲一表示確認號字段有效。此外,TCP 還規定在連接建立後傳送的所有報文段都必須把 ACK 置爲一。
- PSH=1:該字段爲一表示接收端應該立即將數據 push 給應用層,而不是等到緩衝區滿後再提交。
- RST=1:該字段爲一表示當前 TCP 連接出現嚴重問題,可能需要重新建立 TCP 連接,也可以用於拒絕非法的報文段和拒絕連接請求。
- SYN=1:當SYN=1,ACK=0時,表示當前報文段是一個連接請求報文。當SYN=1,ACK=1時,表示當前報文段是一個同意建立連接的應答報文。
- FIN=1:該字段爲一表示此報文段是一個釋放連接的請求報文。
狀態機
HTTP 是無連接的,所以作爲下層的 TCP 協議也是無連接的,雖然看似 TCP 將兩端連接了起來,但是其實只是兩端共同維護了一個狀態
TCP 的狀態機是很複雜的,並且與建立斷開連接時的握手息息相關,接下來就來詳細描述下兩種握手。
在這之前需要了解一個重要的性能指標 RTT。該指標表示發送端發送數據到接收到對端數據所需的往返時間。
建立連接三次握手
在 TCP 協議中,主動發起請求的一端爲客戶端,被動連接的一端稱爲服務端。不管是客戶端還是服務端,TCP 連接建立完後都能發送和接收數據,所以 TCP 也是一個全雙工的協議。
起初,兩端都爲 CLOSED 狀態。在通信開始前,雙方都會創建 TCB。 服務器創建完 TCB 後遍進入 LISTEN 狀態,此時開始等待客戶端發送數據。
第一次握手
客戶端向服務端發送連接請求報文段。該報文段中包含自身的數據通訊初始序號。請求發送後,客戶端便進入 SYN-SENT 狀態,x
表示客戶端的數據通信初始序號。
第二次握手
服務端收到連接請求報文段後,如果同意連接,則會發送一個應答,該應答中也會包含自身的數據通訊初始序號,發送完成後便進入 SYN-RECEIVED 狀態。
第三次握手
當客戶端收到連接同意的應答後,還要向服務端發送一個確認報文。客戶端發完這個報文段後便進入ESTABLISHED 狀態,服務端收到這個應答後也進入 ESTABLISHED 狀態,此時連接建立成功。
PS:第三次握手可以包含數據,通過 TCP 快速打開(TFO)技術。其實只要涉及到握手的協議,都可以使用類似 TFO 的方式,客戶端和服務端存儲相同 cookie,下次握手時發出 cookie 達到減少 RTT 的目的。
你是否有疑惑明明兩次握手就可以建立起連接,爲什麼還需要第三次應答?
因爲這是爲了防止失效的連接請求報文段被服務端接收,從而產生錯誤。
可以想象如下場景。客戶端發送了一個連接請求 A,但是因爲網絡原因造成了超時,這時 TCP 會啓動超時重傳的機制再次發送一個連接請求 B。此時請求順利到達服務端,服務端應答完就建立了請求。如果連接請求 A 在兩端關閉後終於抵達了服務端,那麼這時服務端會認爲客戶端又需要建立 TCP 連接,從而應答了該請求並進入 ESTABLISHED 狀態。此時客戶端其實是 CLOSED 狀態,那麼就會導致服務端一直等待,造成資源的浪費。
PS:在建立連接中,任意一端掉線,TCP 都會重發 SYN 包,一般會重試五次,在建立連接中可能會遇到 SYN FLOOD 攻擊。遇到這種情況你可以選擇調低重試次數或者乾脆在不能處理的情況下拒絕請求。
斷開鏈接四次握手
TCP 是全雙工的,在斷開連接時兩端都需要發送 FIN 和 ACK。
第一次握手
若客戶端 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 不能正常關閉。
ARQ 協議
ARQ 協議也就是超時重傳機制。通過確認和超時機制保證了數據的正確送達,ARQ 協議包含停止等待 ARQ 和連續 ARQ
停止等待 ARQ
正常傳輸過程
只要 A 向 B 發送一段報文,都要停止發送並啓動一個定時器,等待對端迴應,在定時器時間內接收到對端應答就取消定時器併發送下一段報文。
報文丟失或出錯
在報文傳輸的過程中可能會出現丟包。這時候超過定時器設定的時間就會再次發送丟包的數據直到對端響應,所以需要每次都備份發送的數據。
即使報文正常的傳輸到對端,也可能出現在傳輸過程中報文出錯的問題。這時候對端會拋棄該報文並等待 A 端重傳。
PS:一般定時器設定的時間都會大於一個 RTT 的平均時間。
ACK 超時或丟失
對端傳輸的應答也可能出現丟失或超時的情況。那麼超過定時器時間 A 端照樣會重傳報文。這時候 B 端收到相同序號的報文會丟棄該報文並重傳應答,直到 A 端發送下一個序號的報文。
在超時的情況下也可能出現應答很遲到達,這時 A 端會判斷該序號是否已經接收過,如果接收過只需要丟棄應答即可。
這個協議的缺點就是傳輸效率低,在良好的網絡環境下每次發送報文都得等待對端的 ACK 。
連續 ARQ
在連續 ARQ 中,發送端擁有一個發送窗口,可以在沒有收到應答的情況下持續發送窗口內的數據,這樣相比停止等待 ARQ 協議來說減少了等待時間,提高了效率。
累計確認
連續 ARQ 中,接收端會持續不斷收到報文。如果和停止等待 ARQ 中接收一個報文就發送一個應答一樣,就太浪費資源了。通過累計確認,可以在收到多個報文以後統一回復一個應答報文。報文中的 ACK 可以用來告訴發送端這個序號之前的數據已經全部接收到了,下次請發送這個序號 + 1的數據。
但是累計確認也有一個弊端。在連續接收報文時,可能會遇到接收到序號 5 的報文後,並未接到序號 6 的報文,然而序號 7 以後的報文已經接收。遇到這種情況時,ACK 只能回覆 6,這樣會造成發送端重複發送數據,這種情況下可以通過 Sack 來解決,這個會在下文說到。
滑動窗口
在上面小節中講到了發送窗口。在 TCP 中,兩端都維護着窗口:分別爲發送端窗口和接收端窗口。
發送端窗口包含已發送但未收到應答的數據和可以發送但是未發送的數據。
發送端窗口是由接收窗口剩餘大小決定的。接收方會把當前接收窗口的剩餘大小寫入應答報文,發送端收到應答後根據該值和當前網絡擁塞情況設置發送窗口的大小,所以發送窗口的大小是不斷變化的。
當發送端接收到應答報文後,會隨之將窗口進行滑動
滑動窗口實現了流量控制。接收方通過報文告知發送方還可以發送多少數據,從而保證接收方能夠來得及接收數據。
Zero 窗口
在發送報文的過程中,可能會遇到對端出現零窗口的情況。在該情況下,發送端會停止發送數據,並啓動 persistent timer 。該定時器會定時發送請求給對端,讓對端告知窗口大小。在重試次數超過一定次數後,可能會中斷 TCP 鏈接。
擁塞處理
擁塞處理和流量控制不同,後者是作用於接收方,保證接收方來得及接受數據。而前者是作用於網絡,防止過多的數據擁塞網絡,避免出現網絡負載過大的情況。
擁塞處理包括了四個算法,分別爲:慢開始,擁塞避免,快速重傳,快速恢復。
慢開始算法
慢開始算法,顧名思義,就是在傳輸開始時將發送窗口慢慢指數級擴大,從而避免一開始就傳輸大量數據導致網絡擁塞。
慢開始算法步驟具體如下
- 連接初始設置擁塞窗口(Congestion Window) 爲 1 MSS(一個分段的最大數據量)
- 每過一個 RTT 就將窗口大小乘二
- 指數級增長肯定不能沒有限制的,所以有一個閾值限制,當窗口大小大於閾值時就會啓動擁塞避免算法。
擁塞避免算法
擁塞避免算法相比簡單點,每過一個 RTT 窗口大小隻加一,這樣能夠避免指數級增長導致網絡擁塞,慢慢將大小調整到最佳值。
在傳輸過程中可能定時器超時的情況,這時候 TCP 會認爲網絡擁塞了,會馬上進行以下步驟:
- 將閾值設爲當前擁塞窗口的一半
- 將擁塞窗口設爲 1 MSS
- 啓動擁塞避免算法
快速重傳
快速重傳一般和快恢復一起出現。一旦接收端收到的報文出現失序的情況,接收端只會回覆最後一個順序正確的報文序號(沒有 Sack 的情況下)。如果收到三個重複的 ACK,無需等待定時器超時再重發而是啓動快速重傳。具體算法分爲兩種:
TCP Taho 實現如下
- 將閾值設爲當前擁塞窗口的一半
- 將擁塞窗口設爲 1 MSS
- 重新開始慢開始算法
TCP Reno 實現如下
- 擁塞窗口減半
- 將閾值設爲當前擁塞窗口
- 進入快恢復階段(重發對端需要的包,一旦收到一個新的 ACK 答覆就退出該階段)
- 使用擁塞避免算法
TCP New Ren 改進後的快恢復
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 的應答,這時發送端認爲這個分段接收端已經順利接收,接下來會退出快恢復階段。
HTTP
HTTP 協議是個無狀態協議,不會保存狀態。
Post 和 Get 的區別
先引入副作用和冪等的概念。
副作用指對服務器上的資源做改變,搜索是無副作用的,註冊是副作用的。
冪等指發送 M 和 N 次請求(兩者不相同且都大於 1),服務器上資源的狀態一致,比如註冊 10 個和 11 個帳號是不冪等的,對文章進行更改 10 次和 11 次是冪等的。
在規範的應用場景上說,Get 多用於無副作用,冪等的場景,例如搜索關鍵字。Post 多用於副作用,不冪等的場景,例如註冊。
在技術上說:
- Get 請求能緩存,Post 不能
- Post 相對 Get 安全一點點,因爲Get 請求都包含在 URL 裏,且會被瀏覽器保存歷史紀錄,Post 不會,但是在抓包的情況下都是一樣的。
- Post 可以通過 request body來傳輸比 Get 更多的數據,Get 沒有這個技術
- URL有長度限制,會影響 Get 請求,但是這個長度限制是瀏覽器規定的,不是 RFC 規定的
- Post 支持更多的編碼類型且不對數據類型限制
常見狀態碼
2XX 成功
- 200 OK,表示從客戶端發來的請求在服務器端被正確處理
- 204 No content,表示請求成功,但響應報文不含實體的主體部分
- 205 Reset Content,表示請求成功,但響應報文不含實體的主體部分,但是與 204 響應不同在於要求請求方重置內容
- 206 Partial Content,進行範圍請求
3XX 重定向
- 301 moved permanently,永久性重定向,表示資源已被分配了新的 URL
- 302 found,臨時性重定向,表示資源臨時被分配了新的 URL
- 303 see other,表示資源存在着另一個 URL,應使用 GET 方法獲取資源
- 304 not modified,表示服務器允許訪問資源,但因發生請求未滿足條件的情況
- 307 temporary redirect,臨時重定向,和302含義類似,但是期望客戶端保持請求方法不變向新的地址發出請求
4XX 客戶端錯誤
- 400 bad request,請求報文存在語法錯誤
- 401 unauthorized,表示發送的請求需要有通過 HTTP 認證的認證信息
- 403 forbidden,表示對請求資源的訪問被服務器拒絕
- 404 not found,表示在服務器上沒有找到請求的資源
5XX 服務器錯誤
- 500 internal sever error,表示服務器端在執行請求時發生了錯誤
- 501 Not Implemented,表示服務器不支持當前請求所需要的某個功能
- 503 service unavailable,表明服務器暫時處於超負載或正在停機維護,無法處理請求
HTTP 首部
通用字段 | 作用 |
---|---|
Cache-Control | 控制緩存的行爲 |
Connection | 瀏覽器想要優先使用的連接類型,比如 keep-alive
|
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 | 內容的最後修改時間 |
PS:緩存相關已在別的模塊中寫完,你可以 閱讀該小節
HTTPS
HTTPS 還是通過了 HTTP 來傳輸信息,但是信息通過 TLS 協議進行了加密。
TLS
TLS 協議位於傳輸層之上,應用層之下。首次進行 TLS 協議傳輸需要兩個 RTT ,接下來可以通過 Session Resumption 減少到一個 RTT。
在 TLS 中使用了兩種加密技術,分別爲:對稱加密和非對稱加密。
對稱加密:
對稱加密就是兩邊擁有相同的祕鑰,兩邊都知道如何將密文加密解密。
非對稱加密:
有公鑰私鑰之分,公鑰所有人都可以知道,可以將數據用公鑰加密,但是將數據解密必須使用私鑰解密,私鑰只有分發公鑰的一方纔知道。
TLS 握手過程如下圖:
- 客戶端發送一個隨機值,需要的協議和加密方式
- 服務端收到客戶端的隨機值,自己也產生一個隨機值,並根據客戶端需求的協議和加密方式來使用對應的方式,發送自己的證書(如果需要驗證客戶端證書需要說明)
- 客戶端收到服務端的證書並驗證是否有效,驗證通過會再生成一個隨機值,通過服務端證書的公鑰去加密這個隨機值併發送給服務端,如果服務端需要驗證客戶端證書的話會附帶證書
- 服務端收到加密過的隨機值並使用私鑰解密獲得第三個隨機值,這時候兩端都擁有了三個隨機值,可以通過這三個隨機值按照之前約定的加密方式生成密鑰,接下來的通信就可以通過該密鑰來加密解密
通過以上步驟可知,在 TLS 握手階段,兩端使用非對稱加密的方式來通信,但是因爲非對稱加密損耗的性能比對稱加密大,所以在正式傳輸數據時,兩端使用對稱加密的方式通信。
PS:以上說明的都是 TLS 1.2 協議的握手情況,在 1.3 協議中,首次建立連接只需要一個 RTT,後面恢復連接不需要 RTT 了。
HTTP 2.0
HTTP 2.0 相比於 HTTP 1.X,可以說是大幅度提高了 web 的性能。
在 HTTP 1.X 中,爲了性能考慮,我們會引入雪碧圖、將小圖內聯、使用多個域名等等的方式。這一切都是因爲瀏覽器限制了同一個域名下的請求數量,當頁面中需要請求很多資源的時候,隊頭阻塞(Head of line blocking)會導致在達到最大請求數量時,剩餘的資源需要等待其他資源請求完成後才能發起請求。
你可以通過 該鏈接 感受下 HTTP 2.0 比 HTTP 1.X 到底快了多少。
在 HTTP 1.X 中,因爲隊頭阻塞的原因,你會發現請求是這樣的
在 HTTP 2.0 中,因爲引入了多路複用,你會發現請求是這樣的
二進制傳輸
HTTP 2.0 中所有加強性能的核心點在於此。在之前的 HTTP 版本中,我們是通過文本的方式傳輸數據。在 HTTP 2.0 中引入了新的編碼機制,所有傳輸的數據都會被分割,並採用二進制格式編碼。
多路複用
在 HTTP 2.0 中,有兩個非常重要的概念,分別是幀(frame)和流(stream)。
幀代表着最小的數據單位,每個幀會標識出該幀屬於哪個流,流也就是多個幀組成的數據流。
多路複用,就是在一個 TCP 連接中可以存在多條流。換句話說,也就是可以發送多個請求,對端可以通過幀中的標識知道屬於哪個請求。通過這個技術,可以避免 HTTP 舊版本中的隊頭阻塞問題,極大的提高傳輸性能。
Header 壓縮
在 HTTP 1.X 中,我們使用文本的形式傳輸 header,在 header 攜帶 cookie 的情況下,可能每次都需要重複傳輸幾百到幾千的字節。
在 HTTP 2.0 中,使用了 HPACK 壓縮格式對傳輸的 header 進行編碼,減少了 header 的大小。並在兩端維護了索引表,用於記錄出現過的 header ,後面在傳輸過程中就可以傳輸已經記錄過的 header 的鍵名,對端收到數據後就可以通過鍵名找到對應的值。
服務端 Push
在 HTTP 2.0 中,服務端可以在客戶端某個請求後,主動推送其他資源。
可以想象以下情況,某些資源客戶端是一定會請求的,這時就可以採取服務端 push 的技術,提前給客戶端推送必要的資源,這樣就可以相對減少一點延遲時間。當然在瀏覽器兼容的情況下你也可以使用 prefetch 。
QUIC
這是一個谷歌出品的基於 UDP 實現的同爲傳輸層的協議,目標很遠大,希望替代 TCP 協議。
- 該協議支持多路複用,雖然 HTTP 2.0 也支持多路複用,但是下層仍是 TCP,因爲 TCP 的重傳機制,只要一個包丟失就得判斷丟失包並且重傳,導致發生隊頭阻塞的問題,但是 UDP 沒有這個機制
- 實現了自己的加密協議,通過類似 TCP 的 TFO 機制可以實現 0-RTT,當然 TLS 1.3 已經實現了 0-RTT 了
- 支持重傳和糾錯機制(向前恢復),在只丟失一個包的情況下不需要重傳,使用糾錯機制恢復丟失的包
- 糾錯機制:通過異或的方式,算出發出去的數據的異或值並單獨發出一個包,服務端在發現有一個包丟失的情況下,通過其他數據包和異或值包算出丟失包
- 在丟失兩個包或以上的情況就使用重傳機制,因爲算不出來了
DNS
DNS 的作用就是通過域名查詢到具體的 IP。
因爲 IP 存在數字和英文的組合(IPv6),很不利於人類記憶,所以就出現了域名。你可以把域名看成是某個 IP 的別名,DNS 就是去查詢這個別名的真正名稱是什麼。
在 TCP 握手之前就已經進行了 DNS 查詢,這個查詢是操作系統自己做的。當你在瀏覽器中想訪問 www.google.com
時,會進行一下操作:
- 操作系統會首先在本地緩存中查詢
- 沒有的話會去系統配置的 DNS 服務器中查詢
- 如果這時候還沒得話,會直接去 DNS 根服務器查詢,這一步查詢會找出負責
com
這個一級域名的服務器 - 然後去該服務器查詢
google
這個二級域名 - 接下來三級域名的查詢其實是我們配置的,你可以給
www
這個域名配置一個 IP,然後還可以給別的三級域名配置一個 IP
以上介紹的是 DNS 迭代查詢,還有種是遞歸查詢,區別就是前者是由客戶端去做請求,後者是由系統配置的 DNS 服務器做請求,得到結果後將數據返回給客戶端。
PS:DNS 是基於 UDP 做的查詢。
從輸入 URL 到頁面加載完成的過程
這是一個很經典的面試題,在這題中可以將本文講得內容都串聯起來。
- 首先做 DNS 查詢,如果這一步做了智能 DNS 解析的話,會提供訪問速度最快的 IP 地址回來
- 接下來是 TCP 握手,應用層會下發數據給傳輸層,這裏 TCP 協議會指明兩端的端口號,然後下發給網絡層。網絡層中的 IP 協議會確定 IP 地址,並且指示了數據傳輸中如何跳轉路由器。然後包會再被封裝到數據鏈路層的數據幀結構中,最後就是物理層面的傳輸了
- TCP 握手結束後會進行 TLS 握手,然後就開始正式的傳輸數據
- 數據在進入服務端之前,可能還會先經過負責負載均衡的服務器,它的作用就是將請求合理的分發到多臺服務器上,這時假設服務端會響應一個 HTML 文件
- 首先瀏覽器會判斷狀態碼是什麼,如果是 200 那就繼續解析,如果 400 或 500 的話就會報錯,如果 300 的話會進行重定向,這裏會有個重定向計數器,避免過多次的重定向,超過次數也會報錯
- 瀏覽器開始解析文件,如果是 gzip 格式的話會先解壓一下,然後通過文件的編碼格式知道該如何去解碼文件
- 文件解碼成功後會正式開始渲染流程,先會根據 HTML 構建 DOM 樹,有 CSS 的話會去構建 CSSOM 樹。如果遇到
script
標籤的話,會判斷是否存在async
或者defer
,前者會並行進行下載並執行 JS,後者會先下載文件,然後等待 HTML 解析完成後順序執行,如果以上都沒有,就會阻塞住渲染流程直到 JS 執行完畢。遇到文件下載的會去下載文件,這裏如果使用 HTTP 2.0 協議的話會極大的提高多圖的下載效率。 - 初始的 HTML 被完全加載和解析後會觸發
DOMContentLoaded
事件 - CSSOM 樹和 DOM 樹構建完成後會開始生成 Render 樹,這一步就是確定頁面元素的佈局、樣式等等諸多方面的東西
- 在生成 Render 樹的過程中,瀏覽器就開始調用 GPU 繪製,合成圖層,將內容顯示在屏幕上了