文章轉載自:從輸入URL到頁面加載完成的過程當中都發生了什麼事情html
爲何會想要了解從輸入URL到頁面加載完成的過程當中都發生了什麼事情這個問題呢,由於課程參考資料的Web 建站技術中HTML、HTML五、XHTML、CSS、SQL、JavaScript、PHP、ASP.NET、Web Services 是什麼中最高票答案中給出了下圖所示的網站訪問基本過程,張秋怡學姐的解答也十分易懂: 面試
再者這個問題可謂是常見的面試題之一,而這張圖中只是給出了很是基本的一個先後端交互的過程,因爲本身有基礎,因此列出的相關概念也都基本理解了,因而就花些時間擴展一下數據庫
咱們在打開瀏覽器,而後在輸入URL
的時候有沒有發現瀏覽器會給你一些你似曾相識且與你輸入的內容相匹配的網址呢?其實咱們在瀏覽器中輸入URL
的時候,瀏覽器就會開始智能的匹配可能URL
,瀏覽器會從歷史記錄,書籤等地方,找到你已經輸入的字符串可能對應的URL
,而後給出智能提示segmentfault
在輸好URL後咱們會按下Enter鍵,瀏覽器會發起請求,若是URL是域名而不是IP地址,將進行域名解析,所謂域名解析是指什麼呢?windows
IP地址是網絡上標識站點的數字地址,爲了方便記憶,採用域名來代替IP地址標識站點地址,域名解析就是域名到IP地址的轉換過程。後端
域名解析按下面的步驟進行(部份內容涉及到計算機網絡知識):瀏覽器
C:\Windows\System32\drivers\etc
)文件,做用是將一些經常使用的網址域名與其對應的IP地址創建一個關聯「數據庫」。通常來講,系統會首先自動從hosts文件中尋找對應的IP地址,若是有的話就直接使用hosts文件裏面的IP地址,而後直接進行端口確認DNS
服務器的一個客戶,把待解析的域名放在DNS
請求報文中,以UDP用戶數據報的方式發給本地DNS
服務器DNS
服務器查找到相應的域名的IP地址,就把對應的IP地址放在回答報文中返回遞歸查詢: 在該模式下DNS服務器接收到客戶機請求,必須返回一個準確的查詢結果給客戶機。若是該DNS服務器本地沒有存儲被查詢的DNS信息,那麼該服務器會(替客戶機)詢問其餘服務器,並將返回的查詢結果再返回給客戶機。緩存
迭代查詢: 在該模式下DNS服務器接收到客戶機請求,若是該DNS服務器本地沒有存儲被查詢的DNS信息,DNS服務器會向客戶機提供其餘可以解析查詢請求的DNS服務器地址,讓客戶機再向這臺DNS服務器提交請求,依次循環直到返回查詢的結果爲止。服務器
瀏覽器獲得IP地址後,還要確認一下端口,默認端口是80端口,一個服務器可能會提供不一樣的服務,這些服務經過端口來區分,能夠指定端口號網絡
瀏覽器獲得IP地址並確認端口後,會向目標服務器發起HTTP請求,HTTP請求是經過TCP鏈接來發送的(若是是HTTPS則須要先創建SSL鏈接,再是TCP鏈接,下面的討論基於HTTP),具體以下
通俗的能夠理解爲:
A主動向B打電話:嗨,能聽到嗎(SYN=1,seq=x),而後A就開始等待B的回答(SYN-SENT狀態),此時A不知道B能不能聽到
B聽到A的話以後,能夠確認它能聽到A,可是它還要確認一下A能不能聽到他本身的聲音,因而B說:我能聽到你的聲音(ACK=1,ack=x+1),你能聽到個人聲音嗎(SYN=1,seq=y),而後B開始等待A的恢復(SYN-RECD狀態)
A聽到B的話以後,A能夠確認兩件事,一是B能聽到它說話,二是它也能聽到B說話,A已經能夠隨時說話和傾聽了(ESTABLISHED狀態)。可是此時的B還在等待中,並不知道A能不能聽到,因此此時A須要再回復B說:我能夠聽到你的聲音(ACK=1,ack=y+1),開始愉快的聊天吧~(seq=x+1),B聽到這句話後便也能夠隨時說話和傾聽了(ESTABLISHED狀態)
以後兩我的就能夠balabalabala....
有一種觀點是三次握手是基於TCP協議的可靠性(
Reliability
)要求,這是確認雙發都能進行收發的最小次數,兩次確認不了,四次多餘。可是並無徹底意義上的可靠,不論握手多少次都只能代表握手的時候是可靠的,不能保證後面數據傳輸時一直可靠,由於信道是不可靠的,固然三次握手至少能夠代表它曾經可靠,這是兩次握手沒法完成的,而四次甚至更屢次握手僅僅是提升「它曾經可靠」這個結論的可信程度。因此這個握手也只是確保可靠的一個基本須要,TCP協議的可靠性(注意區分完整性integrity
)更多的是由校驗和、定時器超時重傳、確認機制.
在《計算機網絡》一書中也有講過這個問題,給出的解釋是:三次握手是爲了防止失效的鏈接請求報文段被服務端接收,從而產生錯誤。具體例子以下所述:
client
發出的一個鏈接請求報文段並無丟失,而是在某個網絡結點長時間的滯留了,以至延誤到鏈接釋放之後的某個時間纔到達server
。原本這是一個早已失效的報文段。但server
收到此失效的鏈接請求報文段後,就誤認爲是client
再次發出的一個新的鏈接請求。因而就向client
發出確認報文段,贊成創建鏈接。- 假設不採用**「三次握手」**,那麼只要
server
發出確認,新的鏈接就創建了。可是因爲如今client
並無發出創建鏈接的請求,所以不會理睬server
的確認,也不會向server
發送數據。而server
卻覺得新的鏈接已經創建,並一直等待client
發來數據。這樣,server
的不少資源就白白浪費掉了。- 採用**「三次握手」**的辦法能夠防止上述現象發生。例如剛纔那種狀況,
client
不會向server
的確認發出確認。server
因爲收不到確認,就知道client
並無要求創建鏈接
一樣通俗的解釋一波:
A對B要傳的文件已經傳完了,因而他對B說:我要傳的文件已經傳完了,我要準備下線了(seq=u,FIN=1)。而後A就等待B的回覆(FIN-WAIT-1狀態)
B看到A的消息後,回覆A說:知道了,可是我還有文件給你(ACK=1,ack=u+1,seq=v)。B進入等他文件傳完的狀態(CLOSE-WAIT狀態)。A收到B的回覆以後,下線不了了,因而繼續等待着B的文件傳完(FIN-WAIT-2狀態)
幾分鐘後,B的文件傳完了,此時他對A說:個人文件傳完了,我也要下線了(seq=w,FIN=1,ACK=1,ack=u+1),而後B等待A的回覆來確認真的能夠下線了(LAST-ACK狀態)
A收到B的回覆後,便對A說:好的,那你下線吧(ACK=1,seq=u+1,ack=w+1)。此時A會等待一段時間(2MSL,TIME-WAIT狀態),B收到後就直接下線了(CLOSE狀態),而後2MSL時間到了以後,A也下線(CLOSE狀態)
擴展知識小課堂:
當服務器B收到斷開鏈接的請求時,服務器可能仍然有數據未發送完畢,因此服務器先發送確認信號,等全部數據發送完畢後再贊成斷開
由於TCP鏈接是全雙工模式,服務器B收到A的斷開請求時,僅僅代表A沒有東西傳給服務器B了,但此時服務器B可能向A的傳輸還沒結束,因此服務器B要先給A一個確認收到A的斷開請求的ACK報文,而後繼續向A把信息傳完,等傳完以後服務器B再向A發送斷開請求的報文段,等A收到並回復ACK報文後再釋放鏈接。
也就是說對於A來講他要發送請求給B並等待B確認,對於B來講也要發送請求給A並等待A確認,二者都通過這兩個過程才能徹底釋放TCP鏈接,而非單方面的釋放。 創建鏈接只須要創建,沒有數據的影響,而釋放鏈接還要考慮數據是否傳輸完,因此創建鏈接的時候B確認收到A的創建請求與B發送創建請求這一步能夠合成一步成爲TCP創建鏈接的第二次握手,而釋放鏈接時卻必須分開。
2MSL
?首先解釋一下MSL,MSL是指最長報文段壽命,RFC793建議爲兩分鐘,但實際上可據實際狀況而定,也就是說一個報文段最久可存在的時間是MSL
1xx: 表示通知信息的,好比請求收到了或正在處理
2xx:表示成功,操做被成功接收並處理
3xx:表示重定向,通常完成請求還必須採起進一步的行動
4xx:表示客戶端的差錯
5xx:表示服務器的差錯
若是響應可緩存,瀏覽器將把響應存入緩存
瀏覽器根據HTTP報頭信息解碼響應,決定如何處理這些響應,並展示響應,以響應爲一個HTML
爲例 瀏覽器開始自上而下,自左而右的加載HTML
文檔,最開始會遇到 <!DOCTYPE>
聲明,而後根據<!DOCTYPE>
聲明瀏覽器就知道該用哪一種規範來解析這個文檔
再繼續邊加載邊解析,邊生成DOM樹,加載過程當中遇到外部CSS文件,瀏覽器便會另外發出一個請求,來獲取CSS文件(過程和上面說的同樣),獲取CSS後會生成CSS Rule
樹。DOM樹和CSS Rule樹生成Render樹,頁面能夠開始邊加載邊渲染了
<head>…</head>,display=none
的元素)不會被插入渲染樹中;還有像一些節點是絕對定位或浮動,這些節點會在文本流以外,所以他們會在渲染樹和DOM樹的不一樣位置,渲染樹標識出真實的位置,並用一個佔位結構標識出他們原來的位置,而DOM樹上是他們原來的位置layout
)和"繪製"(paint
)這兩個步驟,所謂"佈局"是指給出每一個DOM節點在瀏覽器窗口中的準確位置,"繪製"是指遍歷Render樹將佈局好的DOM節點繪製在屏幕上。<script>
標籤,瀏覽器會當即執行(暫不考慮defer及async屬性),此時會出現頁面阻塞,不只要等待文檔中JS文件下載加載完畢,還要等待JS解析執行完畢,才能夠恢復HTML文檔的加載解析。
Reflow
(稱爲迴流或者重排)<script>
放在的<head>
中,則<body>
標籤沒法被加載,那麼頁面天然就沒法渲染了,所以這將致使在該JS代碼徹底執行完以前,頁面都是一片空白,用戶體驗很是很差,通常我看到長時間的空白頁面,我都很是想直接關閉它。所以會推薦將全部<script>
標籤儘量放到<body>
標籤的底部,以儘可能減小對整個頁面下載的影響,此時雖然還會存在一個腳本阻塞另外一個腳本的問題,可是用戶體驗比上面的好不少,由於用戶看到了大部份內容,而不是空白DOMContentLoaded
事件觸發以前完成,執行順序爲出現的前後順序。(高程中指出現實中不必定會按照順序執行,也不必定會在DOMContentLoaded
事件觸發以前完成,所以最好只包含一個延遲腳本,這多是與瀏覽器的實現有關,具體什麼狀況下會出現我還不知道???)load
事件觸發以前執行,但可能會在DOMContentLoaded
事件觸發以前或以後執行。基於前面所說的一點緣由,異步腳本最好不要修改DOM,若是由多個異步腳本,它們之間最好沒有依賴關係原本只是想了解了解,結果一入深似海,看似簡單的操做背後藏着數不清的小動做,文中也只是涉及了一部分,還有不少相關的過程沒有涉及到,可是能力有限,仍是慢慢來,暫時就先告一段落,文中若有錯誤還請指正哦~