瀏覽器輸入url地址到顯示主頁的過程
不得不說這是面試裏的一位常客,咱們能夠把這整個構成分紅DNS解析、TCP鏈接、發送HTTP請求、服務器處理請求並返回HTTP報文、瀏覽器解析渲染頁面、鏈接結束這六個階段,接下來詳細講解一下這幾個階段:
其實開始以前,還有幾個過程:那就是瀏覽器要對用戶輸入的網址作初步的格式化檢查(好比baidu@.com1是非法無效的),只有經過了這一步纔會繼續,還有就是瀏覽器會首先查詢自身緩存,若是有的話直接返回,沒有的話繼續下面的過程:css
DNS解析
拿百度爲例,當你輸入www.baidu.com的時候,計算機並不知道它在哪,而是須要知道ip地址才能夠進行請求,也就是說,咱們要作的第一步就是如何經過url地址來得到目的ip地址。
那有些人就有疑問了:既然這麼麻煩,那爲何咱們要使用域名而不是直接使用ip地址,這是由於:git
- IP地址不容易記住,特定的域名,更加方便用戶記憶或辨識
- IP地址與域名並非一對一的映射關係,一個域名能夠對應多臺設備的IP地址,一個IP地址只能對應一個域名
這裏引入一個概念叫作DNS解析,DNS解析就是爲了將網址轉變爲相應的ip地址,其中DNS解析是一個遞歸查詢的過程:github
- 首先,本機先經過DNS服務器在DNS高速緩存和本地硬盤裏的host文件查詢是否有www.baidu.com的ip地址,若是查到直接將結果返回給計算機,結束。反之,若是沒有找到,繼續下一步。
這裏插一句:訪問www.github.com的速度很慢的緣由是github服務器在國外所以會被牆,這個時候能夠嘗試修改本地host文件在最後加上 域名 + ip地址,這樣的話能夠縮短訪問時間。
- 本地域名服務器繼續詢問根域名服務器(.),根域名服務器返回對應的頂級域名服務器(com)地址給本地域名服務器返回
- 本地域名服務器根據頂級域名服務器地址訪問頂級域名服務器(com),頂級域名服務器將baidu.com的地址返回給本地域名服務器
- 本地域名服務器根據baidu.com域名服務器的地址找到baidu.com域名服務器,詢問www.baidu.com的IP,並獲得返回結果
- 本地域名服務器將獲得的結果返回給本機,同時將獲得的地址保存在DNS高速緩存一段時間,避免下次訪問還會經歷不少步驟。
- 本地域名服務器將獲得www.baidu.com的地址給瀏覽器,同時瀏覽器會對www.baidu.com進行緩存,方便下次訪問。
關於DNS,這裏繼續討論一下關於DNS優化的問題:
- DNS緩存,避免DNS解析(涉及到UDP和TCP請求)中進行太多的查找,因此DNS中存在着多級緩存,包括瀏覽器緩存、系統緩存、路由器緩存、根域名服務器緩存、頂級域名服務器緩存等等
- DNS負載均衡,又叫DNS重定向,咱們知道一個網站背後可能有不少機器,DNS服務器會返回給用戶距離他最近的點的IP地址
瀏覽器拿到DNS服務器傳輸回來的ip地址以後,會把ip地址打到協議上,同時請求參數也會在協議搭載,而後發給對應的服務器,HTTP請求分紅三個階段:TCP鏈接、HTTP請求相應信息、TCP斷開鏈接面試
TCP鏈接
TCP三次握手的過程
最初,Client和Server都處於CLOSED(關閉)狀態,本例中Client主動打開鏈接,Server被動打開。Server的TCP服務器首先建立傳輸控制塊TCB,準備接受客戶端進程的鏈接請求,而後Server就處於LISTEN(監聽)狀態,等待客戶端的鏈接請求。
第一次(紅色表示):Client首先建立傳輸控制塊TCB,再打算創建TCP鏈接時,向B發出鏈接請求報文段,客戶端給服務器端發送一個SYN = 1(同步序列號)和一個初始seq序列號。不能攜帶數據
第二次(藍色標識):服務器端返回SYN/ACK標識符,以及Seq和ack,不能攜帶數據
第三次(綠色標識):客戶端收到Server的確認以後,給Server發送確認信息,這是ACK = 1,Seq = X+1,ack = Y + 1。這是,TCP鏈接已經創建,Client進入ESTABLISHED(已創建鏈接)狀態。瀏覽器
爲何是三次,不是兩次或者四次握手
- 爲何不是兩次
爲了防止已經失效的鏈接請求報文段忽然又傳送到了Server,於是產生錯誤,咱們假設一種狀況:Client發出的第一個鏈接請求報文段並無丟失,而是在網絡結點長時間的滯留了,致使它會在鏈接釋放以後的某個時間到達Server。原本這是一個早已失效的報文段,可是Server收到此報文段後,誤覺得Client又發出了一次新的請求,因而就向Client發出確認報文段,贊成建議鏈接。
若是不採起三次握手,Server發出確認以後就默認爲新的鏈接已經創建了,並一直等待Client發來數據,這會致使浪費Server的不少資源。
假如採用了三次握手,因爲Client實際上並無發送創建鏈接的請求,因此會忽略Server發送來的確認,也不會向Server發送數據,Server因爲收不到Client返回的確認信息,知道Client並無要求創建鏈接
- 爲何不是四次
由於經過三次握手,服務端和客戶端均可以相互確認對方的通訊情況,都收到了確認信息,因此再次增長握手次數沒有太大的意義。
爲何要傳回SYN
SYN是TCP/IP創建鏈接的時候的握手信號。客戶端首先向服務端發送一個SYN消息,服務器使用SYN-ACK應答表示接收到了這個信號,這之中接收端傳回SYN,就是爲了告訴發送端,我接收到的信息確實就是你發送的信號。緩存
傳了SYN,爲何還要傳ACK
這是由於雙方的要創建鏈接必需要肯定雙方都可以互相發送信息,傳輸SYN,只能確保客戶端可以正確發送消息給服務端,可是接收方 -> 發送方的通道還須要ACK來進行驗證。服務器
發送HTTP請求
構建HTTP請求報文,關於HTTP請求我會放到別的文章當中來說。網絡
服務器處理請求並返回HTTP報文
服務器接收到這個請求,並根據路徑參數映射到特 定的請求處理器進行處理,並將處理結果及相應的視圖返回給瀏覽器,這裏我也放到別的文章來說。負載均衡
瀏覽器解析渲染頁面
瀏覽器解析並渲染視圖,若遇到對 js 文件、css 文件及圖片等靜 態資源的引用,則重複上述步驟並向服務器請求這些資源;瀏覽器根據其請求到的資源、 數據渲染頁面,終向用戶呈現一個完整的頁面。優化
鏈接結束
咱們知道,當數據發送完畢以後,須要斷開TCP的鏈接,這涉及到TCP斷開鏈接的四次揮手過程,接下來咱們來關注一下TCP斷開鏈接的四次揮手過程以及過程當中遇到的問題。
TCP斷開鏈接四次揮手過程
- 第一次揮手: A 的應用進程先向其 TCP 發出鏈接釋放報文段,並中止再發送數據,主動關閉 TCP 鏈接。A 把鏈接釋放報文段首部的終止控制位 FIN 置 1,其序號 seq = u(等於前面已傳 送過的數據的後一個字節的序號加 1),這時 A 進入 FIN-WAIT-1(終止等待1)狀態,等 待 B 的確認。請注意:TCP 規定,FIN 報文段即便不攜帶數據,也將消耗掉一個序號。
- 第二次揮手: B 收到鏈接釋放報文段後當即發出確認,確認號是 ack = u + 1,而這個報文段本身 的序號是 v(等於 B 前面已經傳送過的數據的後一個字節的序號加1),而後 B 就進入 CLOSE-WAIT(關閉等待)狀態。TCP 服務端進程這時應通知高層應用進程,於是從 A 到 B 這個方向的鏈接就釋放了,這時的 TCP 鏈接處於半關閉(half-close)狀態,即 A 已經沒 有數據要發送了,但 B 若發送數據,A 仍要接收。也就是說,從 B 到 A 這個方向的鏈接並 未關閉,這個狀態可能會持續一段時間。A 收到來自 B 的確認後,就進入 FIN-WAIT-2(終止等待2)狀態,等待 B 發出的鏈接釋放報 文段。
- 第三次揮手: 若 B 已經沒有要向 A 發送的數據,其應用進程就通知 TCP 釋放鏈接。這時 B 發出的 鏈接釋放報文段必須使 FIN = 1。假定 B 的序號爲 w(在半關閉狀態,B 可能又發送了一 些數據)。B 還必須重複上次已發送過的確認號 ack = u + 1。這時 B 就進入 LASTACK(後確認)狀態,等待 A 的確認。
- 第四次揮手: A 在收到 B 的鏈接釋放報文後,必須對此發出確認。在確認報文段中把 ACK 置 1,確 認號 ack = w + 1,而本身的序號 seq = u + 1(前面發送的 FIN 報文段要消耗一個序 號)。而後進入 TIME-WAIT(時間等待) 狀態。請注意,如今 TCP 鏈接尚未釋放掉。必 須通過時間等待計時器設置的時間 2MSL(MSL:長報文段壽命)後,A 才能進入到 CLOSED 狀態,而後撤銷傳輸控制塊,結束此次 TCP 鏈接。固然若是 B 一收到 A 的確認就進入 CLOSED 狀態,而後撤銷傳輸控制塊。因此在釋放鏈接時,B 結束 TCP 鏈接的時 間要早於 A。
爲何 A 在 TIME-WAIT 狀態必須等待 2MSL 的時間呢?
- 爲了保證 A 發送的最後一個 ACK 報文段可以到達 B。這個 ACK 報文段有可能丟失, 於是使處在 LAST-ACK 狀態的 B 收不到對已發送的 FIN + ACK 報文段的確認。B 會超時 重傳這個 FIN+ACK 報文段,而 A 就能在 2MSL 時間內(超時 + 1MSL 傳輸)收到這個 重傳的 FIN+ACK 報文段。接着 A 重傳一次確認,從新啓動 2MSL 計時器。後,A 和 B 都正常進入到 CLOSED 狀態。若是 A 在 TIME-WAIT 狀態不等待一段時間,而是在發送完 ACK 報文段後當即釋放鏈接,那麼就沒法收到 B 重傳的 FIN + ACK 報文段,於是也不會 再發送一次確認報文段,這樣,B 就沒法按照正常步驟進入 CLOSED 狀態。
- 防止已失效的鏈接請求報文段出如今本鏈接中。A 在發送完後一個 ACK 報文段後, 再通過時間 2MSL,就可使本鏈接持續的時間內所產生的全部報文段都從網絡中消失。 這樣就可使下一個鏈接中不會出現這種舊的鏈接請求報文段。
爲何第二次和第三次不能合併,第二次和第三次之間等待的什麼
當服務器執行第二次揮手以後, 此時證實客戶端不會再向服務端請求任何數據, 可是服 務端可能還正在給客戶端發送數據(多是客戶端上一次請求的資源尚未發送完畢), 因此此時服務端會等待把以前未傳輸完的數據傳輸完畢以後再發送關閉請求。
保活計時器
除時間等待計時器外,TCP 還有一個保活計時器(keepalive timer)。設想這樣的 場景:客戶已主動與服務器創建了 TCP 鏈接。但後來客戶端的主機忽然發生故障。顯然,服務器之後就不能再收到客戶端發來的數據。所以,應當有措施使服務器不要再白白等待下去。這就須要使用保活計時器了。
服務器每收到一次客戶的數據,就從新設置保活計時器,時間的設置一般是兩個小 時。若兩個小時都沒有收到客戶端的數據,服務端就發送一個探測報文段,之後則每隔 75 秒鐘發送一次。若連續發送 10個 探測報文段後仍然無客戶端的響應,服務端就認爲客戶端 出了故障,接着就關閉這個鏈接。
本文部份內容參考自:
1.https://zhuanlan.zhihu.com/p/...2.《計算機網絡-自頂向下方法》