前言
當我們在web瀏覽器的地址欄中輸入:www.baidu.com,具體發生了什么?
概述
- 對www.baidu.com這個網址進行DNS域名解析,得到對應的IP地址
- 根據這個IP,找到對應的服務器,發起TCP的三次握手
- 建立TCP連接后發起HTTP請求
- 服務器響應HTTP請求,瀏覽器得到html代碼
- 瀏覽器解析html代碼,並請求html代碼中的資源(如js、css、圖片等)(先得到html代碼,才能去找這些資源)
- 瀏覽器對頁面進行渲染呈現給用戶
- 服務器關閉關閉TCP連接
注:
1.DNS怎么找到域名的?
DNS域名解析采用的是遞歸查詢的方式,過程是,先去找DNS緩存->緩存找不到就去找根域名服務器->根域名又會去找下一級,這樣遞歸查找之后,找到了,給我們的web瀏覽器
2.為什么HTTP協議要基於TCP來實現?
TCP是一個端到端的可靠的面相連接的協議,HTTP基於傳輸層TCP協議不用擔心數據傳輸的各種問題(當發生錯誤時,會重傳)
3.最后一步瀏覽器是如何對頁面進行渲染的?
a)解析html文件構成 DOM樹
b)解析CSS文件構成渲染樹
c)邊解析,邊渲染
d)JS 單線程運行,JS有可能修改DOM結構,意味着JS執行完成前,后續所有資源的下載是沒有必要的,所以JS是單線程,會阻塞后續資源下載
各個步驟具體細節
DNS解析(域名解析服務器)
a)首先會搜索瀏覽器自身的DNS緩存(緩存時間比較短,大概只有1分鍾,且只能容納1000條緩存)
b)如果瀏覽器自身的緩存里面沒有找到,那么瀏覽器會搜索系統自身的DNS緩存
c)如果還沒有找到,那么嘗試從 hosts文件里面去找
d)在前面三個過程都沒獲取到的情況下,就遞歸地去域名服務器去查找,具體過程如下
DNS優化兩個方面:DNS緩存、DNS負載均衡
TCP連接建立(三次握手)
拿到域名對應的IP地址之后,User-Agent(一般指瀏覽器)會以一個隨機端口(1024<端口<65535)向服務器的WEB程序(常用的有httpd,nginx)等的80端口。這個連接請求(原始的http請求經過TCP/IP4層模型的層層封包)到達服務器端后(這中間有各種路由設備,局域網內除外),進入到網卡,然后是進入到內核的TCP/IP協議棧(用於識別連接請求,解封包,一層一層的剝開),還有可能要經過Netfilter防火牆(屬於內核的模塊)的過濾,最終達到WEB程序,最終建立了TCP/IP的連接
發起HTTP請求(建立連接后)
HTTP請求報文由三部分組成:請求行,請求頭、空格、請求正文
請求行:用於描述客戶端的請求方式(GET/POST等),請求的資源名稱(URL)以及使用的HTTP協議的版本號
請求頭:用於描述客戶端請求哪台主機及其端口,以及客戶端的一些環境信息等
空行:空行就是\r\n (POST請求時候有)
請求正文:當使用POST等方法時,通常需要客戶端向服務器傳遞數據。這些數據就儲存在請求正文中(GET方式是保存在url地址后面,不會放到這里)
GET請求
下面是瀏覽器對 http://localhost:8081/test?name=XXG&age=23的GET 請求時發送給服務器的數據:
可以看出請求包含請求行和請求頭兩部分。其中請求行中包含 method(例如 GET、POST)、URI(通一資源標志符)和協議版本三部分,三個部分之間以空格分開。請求行和每個請求頭各占一行,以換行符 CRLF(即 \r\n)分割。
POST請求
下面是瀏覽器對 http://localhost:8081/test 的 POST 請求時發送給服務器的數據,消息體中帶上參數 name=XXG&age=23
可以看出,上面的請求包含三個部分:請求行、請求頭、空格、消息體,比之前的 GET 請求多了一個請求消息,其中 請求頭和消息體之間用一個空行分割。POST 請求的參數不在 URL 中,而是在消息體中,請求頭中多了一項 Content-Length 用於表示消息體的字節數,這樣服務器才能知道請求是否發送結束。這也就是 GET 請求和 POST 請求的主要區別。
那么起始行中的請求方法有哪些種呢?
GET: 完整請求一個資源 (常用)
HEAD: 僅請求響應首部
POST:提交表單 (常用)
PUT: (webdav) 上傳文件(但是瀏覽器不支持該方法)
DELETE:(webdav) 刪除
OPTIONS:返回請求的資源所支持的方法的方法
TRACE: 追求一個資源請求中間所經過的代理(該方法不能由瀏覽器發出)
那什么是URL、URI、URN?
URI Uniform Resource Identifier 統一資源標識符
URL Uniform Resource Locator 統一資源定位符
URN Uniform Resource Name 統一資源名稱
URL和URN 都屬於 URI,為了方便就把URL和URI暫時都通指一個東西
服務器響應http請求,瀏覽器得到html代碼
HTTP響應也由三部分組成:狀態行,響應頭,空格,消息體
狀態行包括:協議版本、狀態碼、狀態碼描述
狀態碼:狀態碼用於表示服務器對請求的處理結果
1xx:指示信息——表示請求已經接受,繼續處理
2xx:成功——表示請求已經被成功接收、理解、接受。
3xx:重定向——要完成請求必須進行更進一步的操作
4xx:客戶端錯誤——請求有語法錯誤或請求無法實現
5xx:服務器端錯誤——服務器未能實現合法的請求。
列舉幾種常見的:
200(沒有問題)
302(要你去找別人)
304(要你去拿緩存)
307(要你去拿緩存)
403(有這個資源,但是沒有訪問權限)
404(服務器沒有這個資源)
500(服務器這邊有問題)
響應頭:響應頭用於描述服務器的基本信息,以及客戶端如何處理數據
空格:CRLF(即 \r\n)分割
消息體:服務器返回給客戶端的數據
響應格式如下圖
上面的 HTTP 響應中,響應頭中的 Content-Length 同樣用於表示消息體的字節數。Content-Type 表示消息體的類型,通常瀏覽網頁其類型是HTML,當然還會有其他類型,比如圖片、視頻等。
瀏覽器解析html代碼,並請求html代碼中的資源
瀏覽器拿到html文件后,就開始解析其中的html代碼,遇到js/css/image等靜態資源時,就向服務器端去請求下載(會使用多線程下載,每個瀏覽器的線程數不一樣),這是時候就用上 keep-alive特性了,建立一次HTTP連接,可以請求多個資源,下載資源的順序就是按照代碼里面的順序,但是由於每個資源大小不一樣,而瀏覽器又是多線程請求請求資源,所以這里顯示的順序並不一定是代碼里面的順序。
瀏覽器對頁面進行渲染呈現給用戶
最后,瀏覽器利用自己內部的工作機制,把請求的靜態資源和html代碼進行渲染,渲染之后呈現給用戶,瀏覽器是一個邊解析邊渲染的過程。首先瀏覽器解析HTML文件構建DOM樹,然后解析CSS文件構建渲染樹,等到渲染樹構建完成后,瀏覽器開始布局渲染樹並將其繪制到屏幕上。這個過程比較復雜,涉及到兩個概念: reflow(回流)和repain(重繪)。DOM節點中的各個元素都是以盒模型的形式存在,這些都需要瀏覽器去計算其位置和大小等,這個過程稱為relow;當盒模型的位置,大小以及其他屬性,如顏色,字體,等確定下來之后,瀏覽器便開始繪制內容,這個過程稱為repain。頁面在首次加載時必然會經歷reflow和repain。reflow和repain過程是非常消耗性能的,尤其是在移動設備上,它會破壞用戶體驗,有時會造成頁面卡頓。所以我們應該盡可能少的減少reflow和repain。JS的解析是由瀏覽器中的JS解析引擎完成的。JS是單線程運行,JS有可能修改DOM結構,意味着JS執行完成前,后續所有資源的下載是沒有必要的,所以JS是單線程,會阻塞后續資源下載。
服務器關閉關閉TCP連接
一般情況下,一旦Web服務器向瀏覽器發送了請求數據,它就要關閉TCP連接,然后如果瀏覽器或者服務器在其頭信息加入了這行代碼:
Connection:keep-alive
TCP連接在發送后將仍然保持打開狀態,於是,瀏覽器可以繼續通過相同的連接發送請求。保持連接節省了為每個請求建立新連接所需的時間,還節約了網絡帶寬。
自此一次完整的HTTP事務宣告完成.