最近在刷前端的筆試和面試題的時候,經常遇到讓簡述一下“從頁面請求到后端響應再到頁面呈現的整個過程”的問題。于是自己通過查找相關大牛的博文后,決定自己動手總結一下,以便以后復習之用。
本文總結主要借鑒自阮一峰的互聯網協議入門(一)、互聯網協議入門(二)、DNS 原理入門。
正文開始
其實在我看來,不管是前端開發還是后端開發的工程師都應該了解這個過程,了解這個過程可以讓你整體把握整個軟件的運行流程,同時可以讓你更好地理解一些前后端優化、SEO,甚至一些網絡安全的問題。
要想講清楚“網絡請求的整個過程”的話,其中涉及到的網絡基礎、HTTP協議、瀏覽器的工作原理等都應該是必備的知識儲備,接下來的文章中,我也會在講清楚整個請求過程的同時穿插補充這些相關知識,已經對這些知識有了解和深入研究的同學可以權當復習了。
DNS解析
當我們在客戶端(瀏覽器)的地址欄輸入一個網址并敲回車的時候,首先會執行的一步操作的就是DNS解析(也叫域名解析)。那什么是DNS解析呢?又為什么要進行DNS解析呢?帶著這兩個問題,我們來看看DNS解析。
DNS(域名系統)
1.DNS是什么?
DNS:Domain Name System(域名系統),互聯網上作為域名和IP地址相互映射的一個分布式數據庫。注意,我加粗了兩個詞“相互映射”和“數據庫”,稍后再解釋為什么要加粗它倆。
2.為什么要DNS解析?
看了DNS的概念,有些同學可能覺著更加迷惑了,不要著急,咱們從DNS解析的過程方面來更好地了解它。要講DNS的解析過程,首先咱們得先來了解一下計算機之間是怎樣進行通信的。
兩臺電腦之間通信
有兩臺電腦A和B,A電腦想向B電腦發送一條信息,那該怎么辦呢?
其實很簡單,就像郵遞員(假設為電腦A)要給某棟大樓內的某個房間的客戶(假設為電腦B)送郵件一樣,郵遞員要想把郵件送到客戶手上,那他必須知道客戶的大樓名稱(或者說地址)以及客戶在這棟大樓內的房間號。在互聯網的世界中也是一樣,電腦A想向電腦B發送一條信息,電腦A就得知道電腦B的MAC地址和IP地址。
補充1:MAC地址
以太網規定,連入網絡的所有設備,都必須具有“網卡”接口。數據包必須是從一塊網卡,傳送到另一塊網卡。網卡的地址,就是數據包的發送地址和接收地址,這叫做MAC地址。
就是說要想上網就得有塊叫做“網卡”的東西,電腦的網卡很好理解,大家經常接觸;而像路由器,交換機,手機,平板等聯網的設備都有“網卡”這個東西。而兩臺設備(也可以理解為電腦)之間的通信就相當于兩塊網卡之間的通信,而這個網卡就是MAC地址,MAC地址就相當于“送郵件”例子中的客戶房間號(啰嗦了一下,怕新手看不懂)。
而每塊網卡出廠的時候,都有一個全世界獨一無二的MAC地址,長度是48個二進制位,通常用12個十六進制數表示。
前6個十六進制數是廠商編號,后6個是該廠商的網卡流水號。有了MAC地址,就可以定位網卡和數據包的路徑了。
至于IP地址就不用再補充了吧,既然學計算機了,肯定知道IP地址。IP地址就相當于“送郵件”例子中的大樓名稱(或者說地址)。
總結:A電腦想向B電腦發送一條信息,首先A電腦要知道B電腦的MAC地址和IP地址,這其中IP地址一般是已知的,而MAC地址是未知的。這時候就需要通過ARP協議來確定B電腦的MAC地址,這其實也包括兩種情況(不做詳細介紹,更多了解請看阮一峰的互聯網協議入門(一))。只要拿到了電腦B的IP地址和MAC地址,兩臺電腦就可以通信了。
域名出現
上面講到一臺電腦要想和另一臺通信就需要知道另一臺電腦的IP地址和MAC地址,MAC地址未知但可以通過ARP協議去獲取,而IP地址是事先知道的,所以就可以通信了。但是,大家都知道IP地址這個東西是個由32位二進制組成的網絡地址(IPv4),即使習慣上把它表示為四段十進制的形式(如202.201.112.232),也是不好記憶的,所以神通廣大的人類就發明了域名來代替IP地址,其實就是給IP地址起了一個別名,這樣就解決了IP地址不好記憶的問題了。
任何一個域名都對應一個或者多個ip地址,但是大部分都是一個域名對應一個ip地址。
DNS小結
現在咱們再來看最初的問題:
DNS:Domain Name System(域名系統),互聯網上作為域名和IP地址相互映射的一個分布式數據庫。
現在應該明白了吧,域名系統就是儲存IP地址和域名映射的一個數據庫。而DNS解析,就是通過這個數據庫去查找到可以使兩臺聯網設備進行通信的IP地址的一個過程。當然這個過程是非常的繁瑣的,想繼續深入研究的同學可以查看阮一峰的DNS 原理入門,現在你可以回答最初前面提到的那個問題了。
DNS是什么?
又為什么需要DNS解析?
HTTP請求
當兩臺聯網設備通過IP地址和MAC地址完成了“鏈接”之后,接下來就是通信了,而通信就需要傳輸數據(哪怕只是一個沒有數據的請求)。要想傳輸數據,就需要建立一個傳輸數據的鏈接(注意,我前面的鏈接加了引號,是因為那個時候兩臺電腦并為真正意義是的鏈接,只是找到對方了)。而這就牽扯到了另一個面試官常問的問題TCP的三次握手和TCP的四次揮手。
補充2:端口
首先來說,UDP和TCP都是傳輸層的協議,不同點就是傳輸的方式不同(簡單的說,后面會詳細說明他們的不同點)。
咱們在前面多次提到過通過IP地址和MAC地址可以建立兩臺電腦的“鏈接”,那么鏈接以后,應該怎樣就行數據的傳輸呢?傳輸的數據是哪一個程序需要的呢(例如:同一臺主機上有許多程序都需要用到網絡,比如,你一邊瀏覽網頁,一邊與朋友在線聊天。當一個數據包從互聯網上發來的時候,你怎么知道,它是表示網頁的內容,還是表示在線聊天的內容?)?于是人們發明了一個叫做“端口”的參數來區別不同程序之間的通信。
"端口"是0到65535之間的一個整數,正好16個二進制位。0到1023的端口被系統占用,用戶只能選用大于1023的端口。不管是瀏覽網頁還是在線聊天,應用程序會隨機選用一個端口,然后與服務器的相應端口聯系。
這樣就可以進行數據的傳輸了。
UDP協議
UDP協議簡單來說就是在數據包中插入一段數據用來標記端口信息,然后將數據發送出去,至于發送出去的數據包有沒有被目標設備接收到,它就不管了。以這種方式發送數據包的有點就是簡單,容易實現,但是缺點就是可靠性差,因為通過UDP協議發送出去的數據包無法確定發送的數據包是否到達目標設備。
TCP協議
而TCP協議就是為了解決UDP協議的缺點而誕生的,它雖然實現上比UDP協議復雜,但是可靠性好,可以保證數據被發送到目標設備上。
TCP三次握手
TCP協議是如何保證可靠性的呢?就是通過三次與目標設備的通信來確定數據包發送成功。以瀏覽器和服務器的通信來打比方:
瀏覽器:你好服務器,我是 瀏覽器A。
服務器:你好 瀏覽器A,我是 服務器B。
瀏覽器:服務器B 你好。
官方描述
- 第一次握手:建立連接時,客戶端發送syn包(syn=j)到服務器,并進入SYN_SENT狀態,等待服務器確認;SYN:同步序列編號(Synchronize Sequence Numbers)。
- 第二次握手:服務器收到syn包,必須確認客戶的SYN(ack=j+1),同時自己也發送一個SYN包(syn=k),即SYN+ACK包,此時服務器進入SYN_RECV狀態;
- 第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=k+1),此包發送完畢,客戶端和服務器進入ESTABLISHED(TCP連接成功)狀態,完成三次握手。
完成三次握手,客戶端與服務器開始傳送數據。這樣就保證了,每次傳送數據都會準確到達目標設備了。
TCP四次揮手
當數據包發送完畢需要斷開連接的時候,就需要TCP的四次揮手來保證鏈接的合理斷開。再次以瀏覽器和服務器的通信打比方:
主動結束方:你好,我的數據發送完畢了,我要進入準備斷開的狀態了。(此時它雖然不再發送數據了,但是可以接受數據)
另一方:我知道了,我還沒有發送完畢的,你等著吧。
另一方:我也發送完畢了,可以斷開鏈接了。(此時它也進入準備斷開的狀態)
主動結束方:好的,那斷開吧。
官方描述
1.客戶端A發送一個FIN,用來關閉客戶A到服務器B的數據傳送。
2.服務器B收到這個FIN,它發回一個ACK,確認序號為收到的序號加1。和SYN一樣,一個FIN將占用一個序號。
3.服務器B關閉與客戶端A的連接,發送一個FIN給客戶端A。
4.客戶端A發回ACK報文確認,并將確認序號設置為收到序號加1。
TCP為什么建立鏈接是三次,關閉鏈接是四次呢?
這是前端面試中在設計HTTP協議問題時,經常會被問的一個問題。其實也不難理解,因為服務端的listen狀態下的socket當收到SYN報文的建連請求后,它可以把ACK和SYN(ACK起應答作用,而SYN起同步作用)放在一個報文里來發送。但關閉連接時,當收到對方的FIN報文通知時,它僅僅表示對方沒有數據發送給你了;但未必你所有的數據都全部發送給對方了,所以你可以未必會馬上會關閉SOCKET,也即你可能還需要發送一些數據給對方之后,再發送FIN報文給對方來表示你同意現在可以關閉連接了,所以它這里的ACK報文和FIN報文多數情況下都是分開發送的。
通過TCP協議使得兩臺設備成功鏈接,并成功發送了數據,接下來,就需要服務器端來處理數據了。
服務器處理數據并返回響應
當服務器成功的接受到了瀏覽器發送的數據之后,接下來就是根據瀏覽器發送過來的數據就行后臺程序的處理。這個過程就是在運行后端代碼,當程序運行完成以后就產生了返回數據包,然后服務器端在通過TCP協議將數據包發送回瀏覽器。
這個過程和前面相比是不是很簡單,其實不然,這個過程也相當復雜,只不過不是本文的重點就不詳細介紹了,貼上一張Struts2的處理流程圖,自己體會一下服務器端的某一個小階段吧。
瀏覽器解析數據并呈現
當服務器返回數據包以后,接下來的工作就交給瀏覽器自己去處理這些數據,最后展示在頁面上。這個過程涉及到了瀏覽器的運行原理的相關只是,本人也不是很懂,所以只是大概記錄一下。
瀏覽器的組成
1. 用戶界面:包括地址欄、后退/前進按鈕、書簽目錄等,也就是你所看到的除了用來顯示你所請求頁面的主窗口之外的其他部分。
2. 瀏覽器引擎:用來查詢及操作渲染引擎的接口。
3. 渲染引擎:用來顯示請求的內容,例如,如果請求內容為html,它負責解析html及css,并將解析后的結果顯示出來。
4. 網絡:用來完成網絡調用,例如http請求,它具有平臺無關的接口,可以在不同平臺上工作。
5. UI后端:用來繪制類似組合選擇框及對話框等基本組件,具有不特定于某個平臺的通用接口,底層使用操作系統的用戶接口。
6. JS解釋器:用來解釋執行JS代碼。
7. 數據存儲:屬于持久層,瀏覽器需要在硬盤中保存類似cookie的各種數據,HTML5定義了web database技術,這是一種輕量級完整的客戶端存儲技術
渲染的流程
可以簡單的描述為以下四部分:
1.解析HTML以構建DOM樹
2.構建render樹
3.布局render樹
4.繪制render樹
但實際上渲染的過程是這樣的:
1.在瀏覽器進行渲染的時候,渲染引擎首先會解析HTML代碼,然后將標簽轉化為DOM樹上的一個個對應節點(我們可以在chorme的Elements面板中查看到)。
2.接著,渲染引擎解析外部CSS文件及style標簽中的樣式信息。這些樣式信息以及HTML中的可見性指令將被用來構建另一棵樹---render樹。Render樹由一些包含有顏色和大小等屬性的矩形組成,它們將被按照正確的順序顯示到屏幕上。
3.Render樹構建好了之后,將會執行布局過程,它將確定每個節點在屏幕上的確切坐標。
4.然后就是繪制,即遍歷render樹,并使用UI后端層繪制每個節點。
值得注意的是,這個過程是逐步完成的,為了更好的用戶體驗,渲染引擎將會盡可能早的將內容呈現到屏幕上,并不會等到所有的html都解析完成之后再去構建和布局render樹。它是解析完一部分內容就顯示一部分內容,同時,可能還在通過網絡下載其余內容。
補充WEBKIT渲染引擎和GECKO渲染引擎
總結
到這里基本上整個網絡請求的過程就結束了,你在瀏覽器地址框輸入了域名并回車,然后通過DNS解析找到相應的IP地址;然后通過HTTP協議建立了鏈接,找到了目標服務器的位置;接著就是TCP三次握手建立可靠鏈接,發送數據,服務器處理數據,TCP四次揮手斷開鏈接;最后瀏覽器根據返回的數據解析渲染呈現頁面。