最近在刷前端的筆試和面試題的時候,經(jīng)常遇到讓簡述一下“從頁面請求到后端響應再到頁面呈現(xiàn)的整個過程”的問題。于是自己通過查找相關大牛的博文后,決定自己動手總結一下,以便以后復習之用。
本文總結主要借鑒自阮一峰的互聯(lián)網(wǎng)協(xié)議入門(一)、互聯(lián)網(wǎng)協(xié)議入門(二)、DNS 原理入門。
正文開始
其實在我看來,不管是前端開發(fā)還是后端開發(fā)的工程師都應該了解這個過程,了解這個過程可以讓你整體把握整個軟件的運行流程,同時可以讓你更好地理解一些前后端優(yōu)化、SEO,甚至一些網(wǎng)絡安全的問題。
要想講清楚“網(wǎng)絡請求的整個過程”的話,其中涉及到的網(wǎng)絡基礎、HTTP協(xié)議、瀏覽器的工作原理等都應該是必備的知識儲備,接下來的文章中,我也會在講清楚整個請求過程的同時穿插補充這些相關知識,已經(jīng)對這些知識有了解和深入研究的同學可以權當復習了。
DNS解析
當我們在客戶端(瀏覽器)的地址欄輸入一個網(wǎng)址并敲回車的時候,首先會執(zhí)行的一步操作的就是DNS解析(也叫域名解析)。那什么是DNS解析呢?又為什么要進行DNS解析呢?帶著這兩個問題,我們來看看DNS解析。
DNS(域名系統(tǒng))
1.DNS是什么?
DNS:Domain Name System(域名系統(tǒng)),互聯(lián)網(wǎng)上作為域名和IP地址相互映射的一個分布式數(shù)據(jù)庫。注意,我加粗了兩個詞“相互映射”和“數(shù)據(jù)庫”,稍后再解釋為什么要加粗它倆。
2.為什么要DNS解析?
看了DNS的概念,有些同學可能覺著更加迷惑了,不要著急,咱們從DNS解析的過程方面來更好地了解它。要講DNS的解析過程,首先咱們得先來了解一下計算機之間是怎樣進行通信的。
兩臺電腦之間通信
有兩臺電腦A和B,A電腦想向B電腦發(fā)送一條信息,那該怎么辦呢?
其實很簡單,就像郵遞員(假設為電腦A)要給某棟大樓內(nèi)的某個房間的客戶(假設為電腦B)送郵件一樣,郵遞員要想把郵件送到客戶手上,那他必須知道客戶的大樓名稱(或者說地址)以及客戶在這棟大樓內(nèi)的房間號。在互聯(lián)網(wǎng)的世界中也是一樣,電腦A想向電腦B發(fā)送一條信息,電腦A就得知道電腦B的MAC地址和IP地址。
補充1:MAC地址
以太網(wǎng)規(guī)定,連入網(wǎng)絡的所有設備,都必須具有“網(wǎng)卡”接口。數(shù)據(jù)包必須是從一塊網(wǎng)卡,傳送到另一塊網(wǎng)卡。網(wǎng)卡的地址,就是數(shù)據(jù)包的發(fā)送地址和接收地址,這叫做MAC地址。
就是說要想上網(wǎng)就得有塊叫做“網(wǎng)卡”的東西,電腦的網(wǎng)卡很好理解,大家經(jīng)常接觸;而像路由器,交換機,手機,平板等聯(lián)網(wǎng)的設備都有“網(wǎng)卡”這個東西。而兩臺設備(也可以理解為電腦)之間的通信就相當于兩塊網(wǎng)卡之間的通信,而這個網(wǎng)卡就是MAC地址,MAC地址就相當于“送郵件”例子中的客戶房間號(啰嗦了一下,怕新手看不懂)。
而每塊網(wǎng)卡出廠的時候,都有一個全世界獨一無二的MAC地址,長度是48個二進制位,通常用12個十六進制數(shù)表示。
前6個十六進制數(shù)是廠商編號,后6個是該廠商的網(wǎng)卡流水號。有了MAC地址,就可以定位網(wǎng)卡和數(shù)據(jù)包的路徑了。

至于IP地址就不用再補充了吧,既然學計算機了,肯定知道IP地址。IP地址就相當于“送郵件”例子中的大樓名稱(或者說地址)。
總結:A電腦想向B電腦發(fā)送一條信息,首先A電腦要知道B電腦的MAC地址和IP地址,這其中IP地址一般是已知的,而MAC地址是未知的。這時候就需要通過ARP協(xié)議來確定B電腦的MAC地址,這其實也包括兩種情況(不做詳細介紹,更多了解請看阮一峰的互聯(lián)網(wǎng)協(xié)議入門(一))。只要拿到了電腦B的IP地址和MAC地址,兩臺電腦就可以通信了。
域名出現(xiàn)
上面講到一臺電腦要想和另一臺通信就需要知道另一臺電腦的IP地址和MAC地址,MAC地址未知但可以通過ARP協(xié)議去獲取,而IP地址是事先知道的,所以就可以通信了。但是,大家都知道IP地址這個東西是個由32位二進制組成的網(wǎng)絡地址(IPv4),即使習慣上把它表示為四段十進制的形式(如202.201.112.232),也是不好記憶的,所以神通廣大的人類就發(fā)明了域名來代替IP地址,其實就是給IP地址起了一個別名,這樣就解決了IP地址不好記憶的問題了。
任何一個域名都對應一個或者多個ip地址,但是大部分都是一個域名對應一個ip地址。
DNS小結
現(xiàn)在咱們再來看最初的問題:
DNS:Domain Name System(域名系統(tǒng)),互聯(lián)網(wǎng)上作為域名和IP地址相互映射的一個分布式數(shù)據(jù)庫。
現(xiàn)在應該明白了吧,域名系統(tǒng)就是儲存IP地址和域名映射的一個數(shù)據(jù)庫。而DNS解析,就是通過這個數(shù)據(jù)庫去查找到可以使兩臺聯(lián)網(wǎng)設備進行通信的IP地址的一個過程。當然這個過程是非常的繁瑣的,想繼續(xù)深入研究的同學可以查看阮一峰的DNS 原理入門,現(xiàn)在你可以回答最初前面提到的那個問題了。
DNS是什么?
又為什么需要DNS解析?
HTTP請求
當兩臺聯(lián)網(wǎng)設備通過IP地址和MAC地址完成了“鏈接”之后,接下來就是通信了,而通信就需要傳輸數(shù)據(jù)(哪怕只是一個沒有數(shù)據(jù)的請求)。要想傳輸數(shù)據(jù),就需要建立一個傳輸數(shù)據(jù)的鏈接(注意,我前面的鏈接加了引號,是因為那個時候兩臺電腦并為真正意義是的鏈接,只是找到對方了)。而這就牽扯到了另一個面試官常問的問題TCP的三次握手和TCP的四次揮手。
補充2:端口
首先來說,UDP和TCP都是傳輸層的協(xié)議,不同點就是傳輸?shù)姆绞讲煌ê唵蔚恼f,后面會詳細說明他們的不同點)。
咱們在前面多次提到過通過IP地址和MAC地址可以建立兩臺電腦的“鏈接”,那么鏈接以后,應該怎樣就行數(shù)據(jù)的傳輸呢?傳輸?shù)臄?shù)據(jù)是哪一個程序需要的呢(例如:同一臺主機上有許多程序都需要用到網(wǎng)絡,比如,你一邊瀏覽網(wǎng)頁,一邊與朋友在線聊天。當一個數(shù)據(jù)包從互聯(lián)網(wǎng)上發(fā)來的時候,你怎么知道,它是表示網(wǎng)頁的內(nèi)容,還是表示在線聊天的內(nèi)容?)?于是人們發(fā)明了一個叫做“端口”的參數(shù)來區(qū)別不同程序之間的通信。
"端口"是0到65535之間的一個整數(shù),正好16個二進制位。0到1023的端口被系統(tǒng)占用,用戶只能選用大于1023的端口。不管是瀏覽網(wǎng)頁還是在線聊天,應用程序會隨機選用一個端口,然后與服務器的相應端口聯(lián)系。
這樣就可以進行數(shù)據(jù)的傳輸了。
UDP協(xié)議
UDP協(xié)議簡單來說就是在數(shù)據(jù)包中插入一段數(shù)據(jù)用來標記端口信息,然后將數(shù)據(jù)發(fā)送出去,至于發(fā)送出去的數(shù)據(jù)包有沒有被目標設備接收到,它就不管了。以這種方式發(fā)送數(shù)據(jù)包的有點就是簡單,容易實現(xiàn),但是缺點就是可靠性差,因為通過UDP協(xié)議發(fā)送出去的數(shù)據(jù)包無法確定發(fā)送的數(shù)據(jù)包是否到達目標設備。
TCP協(xié)議
而TCP協(xié)議就是為了解決UDP協(xié)議的缺點而誕生的,它雖然實現(xiàn)上比UDP協(xié)議復雜,但是可靠性好,可以保證數(shù)據(jù)被發(fā)送到目標設備上。
TCP三次握手
TCP協(xié)議是如何保證可靠性的呢?就是通過三次與目標設備的通信來確定數(shù)據(jù)包發(fā)送成功。以瀏覽器和服務器的通信來打比方:
瀏覽器:你好服務器,我是 瀏覽器A。
服務器:你好 瀏覽器A,我是 服務器B。
瀏覽器:服務器B 你好。
官方描述

- 第一次握手:建立連接時,客戶端發(fā)送syn包(syn=j)到服務器,并進入SYN_SENT狀態(tài),等待服務器確認;SYN:同步序列編號(Synchronize Sequence Numbers)。
- 第二次握手:服務器收到syn包,必須確認客戶的SYN(ack=j+1),同時自己也發(fā)送一個SYN包(syn=k),即SYN+ACK包,此時服務器進入SYN_RECV狀態(tài);
- 第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發(fā)送確認包ACK(ack=k+1),此包發(fā)送完畢,客戶端和服務器進入ESTABLISHED(TCP連接成功)狀態(tài),完成三次握手。
完成三次握手,客戶端與服務器開始傳送數(shù)據(jù)。這樣就保證了,每次傳送數(shù)據(jù)都會準確到達目標設備了。
TCP四次揮手
當數(shù)據(jù)包發(fā)送完畢需要斷開連接的時候,就需要TCP的四次揮手來保證鏈接的合理斷開。再次以瀏覽器和服務器的通信打比方:
主動結束方:你好,我的數(shù)據(jù)發(fā)送完畢了,我要進入準備斷開的狀態(tài)了。(此時它雖然不再發(fā)送數(shù)據(jù)了,但是可以接受數(shù)據(jù))
另一方:我知道了,我還沒有發(fā)送完畢的,你等著吧。
另一方:我也發(fā)送完畢了,可以斷開鏈接了。(此時它也進入準備斷開的狀態(tài))
主動結束方:好的,那斷開吧。
官方描述

1.客戶端A發(fā)送一個FIN,用來關閉客戶A到服務器B的數(shù)據(jù)傳送。
2.服務器B收到這個FIN,它發(fā)回一個ACK,確認序號為收到的序號加1。和SYN一樣,一個FIN將占用一個序號。
3.服務器B關閉與客戶端A的連接,發(fā)送一個FIN給客戶端A。
4.客戶端A發(fā)回ACK報文確認,并將確認序號設置為收到序號加1。
TCP為什么建立鏈接是三次,關閉鏈接是四次呢?
這是前端面試中在設計HTTP協(xié)議問題時,經(jīng)常會被問的一個問題。其實也不難理解,因為服務端的listen狀態(tài)下的socket當收到SYN報文的建連請求后,它可以把ACK和SYN(ACK起應答作用,而SYN起同步作用)放在一個報文里來發(fā)送。但關閉連接時,當收到對方的FIN報文通知時,它僅僅表示對方?jīng)]有數(shù)據(jù)發(fā)送給你了;但未必你所有的數(shù)據(jù)都全部發(fā)送給對方了,所以你可以未必會馬上會關閉SOCKET,也即你可能還需要發(fā)送一些數(shù)據(jù)給對方之后,再發(fā)送FIN報文給對方來表示你同意現(xiàn)在可以關閉連接了,所以它這里的ACK報文和FIN報文多數(shù)情況下都是分開發(fā)送的。
通過TCP協(xié)議使得兩臺設備成功鏈接,并成功發(fā)送了數(shù)據(jù),接下來,就需要服務器端來處理數(shù)據(jù)了。
服務器處理數(shù)據(jù)并返回響應
當服務器成功的接受到了瀏覽器發(fā)送的數(shù)據(jù)之后,接下來就是根據(jù)瀏覽器發(fā)送過來的數(shù)據(jù)就行后臺程序的處理。這個過程就是在運行后端代碼,當程序運行完成以后就產(chǎn)生了返回數(shù)據(jù)包,然后服務器端在通過TCP協(xié)議將數(shù)據(jù)包發(fā)送回瀏覽器。
這個過程和前面相比是不是很簡單,其實不然,這個過程也相當復雜,只不過不是本文的重點就不詳細介紹了,貼上一張Struts2的處理流程圖,自己體會一下服務器端的某一個小階段吧。

瀏覽器解析數(shù)據(jù)并呈現(xiàn)
當服務器返回數(shù)據(jù)包以后,接下來的工作就交給瀏覽器自己去處理這些數(shù)據(jù),最后展示在頁面上。這個過程涉及到了瀏覽器的運行原理的相關只是,本人也不是很懂,所以只是大概記錄一下。
瀏覽器的組成
1. 用戶界面:包括地址欄、后退/前進按鈕、書簽目錄等,也就是你所看到的除了用來顯示你所請求頁面的主窗口之外的其他部分。
2. 瀏覽器引擎:用來查詢及操作渲染引擎的接口。
3. 渲染引擎:用來顯示請求的內(nèi)容,例如,如果請求內(nèi)容為html,它負責解析html及css,并將解析后的結果顯示出來。
4. 網(wǎng)絡:用來完成網(wǎng)絡調(diào)用,例如http請求,它具有平臺無關的接口,可以在不同平臺上工作。
5. UI后端:用來繪制類似組合選擇框及對話框等基本組件,具有不特定于某個平臺的通用接口,底層使用操作系統(tǒng)的用戶接口。
6. JS解釋器:用來解釋執(zhí)行JS代碼。
7. 數(shù)據(jù)存儲:屬于持久層,瀏覽器需要在硬盤中保存類似cookie的各種數(shù)據(jù),HTML5定義了web database技術,這是一種輕量級完整的客戶端存儲技術
渲染的流程

可以簡單的描述為以下四部分:
1.解析HTML以構建DOM樹
2.構建render樹
3.布局render樹
4.繪制render樹
但實際上渲染的過程是這樣的:
1.在瀏覽器進行渲染的時候,渲染引擎首先會解析HTML代碼,然后將標簽轉化為DOM樹上的一個個對應節(jié)點(我們可以在chorme的Elements面板中查看到)。
2.接著,渲染引擎解析外部CSS文件及style標簽中的樣式信息。這些樣式信息以及HTML中的可見性指令將被用來構建另一棵樹---render樹。Render樹由一些包含有顏色和大小等屬性的矩形組成,它們將被按照正確的順序顯示到屏幕上。
3.Render樹構建好了之后,將會執(zhí)行布局過程,它將確定每個節(jié)點在屏幕上的確切坐標。
4.然后就是繪制,即遍歷render樹,并使用UI后端層繪制每個節(jié)點。
值得注意的是,這個過程是逐步完成的,為了更好的用戶體驗,渲染引擎將會盡可能早的將內(nèi)容呈現(xiàn)到屏幕上,并不會等到所有的html都解析完成之后再去構建和布局render樹。它是解析完一部分內(nèi)容就顯示一部分內(nèi)容,同時,可能還在通過網(wǎng)絡下載其余內(nèi)容。
補充WEBKIT渲染引擎和GECKO渲染引擎


總結
到這里基本上整個網(wǎng)絡請求的過程就結束了,你在瀏覽器地址框輸入了域名并回車,然后通過DNS解析找到相應的IP地址;然后通過HTTP協(xié)議建立了鏈接,找到了目標服務器的位置;接著就是TCP三次握手建立可靠鏈接,發(fā)送數(shù)據(jù),服務器處理數(shù)據(jù),TCP四次揮手斷開鏈接;最后瀏覽器根據(jù)返回的數(shù)據(jù)解析渲染呈現(xiàn)頁面。