瀏覽器下的一次網絡請求流程

瀏覽器下的一次網絡請求流程

以輸入 http://github.com/appleguardu... web 請求本質上就是一臺主機去另外一臺主機獲取對應的資源.css

1.進程切換

當導航欄輸入 http://github.com/appleguardu... 瀏覽器進程把這個url交給網絡進程處理, 給我去這臺主機下取資源;html

2.解析請求的URL,DNS查詢IP,準備端口

  • URL: 統一資源定位符
protocol: 協議頭, 譬若有http,ftp等
host: 主機域名或IP地址
port: 端口號
path: 目錄路徑
query: 即查詢參數
fragment: 即#後的hash值,通常用來定位到某個位置
  • DNS(域名與IP的映射系統)
因爲不一樣主機之間都是經過IP地址來肯定的,因此首先會進行域名解析,查詢; 
瀏覽器會向域名服務器去查詢http://github.com對應的Ip.

//  Ip查詢也有一套優化機制: 緩存機制(能夠減小網絡請求)
瀏覽器緩存:   瀏覽器會按照必定的頻率緩存 DNS 記錄;
操做系統緩存: 若是瀏覽器緩存中找不到須要的 DNS 記錄,那就去操做系統中找;
路由緩存:     路由器也有 DNS 緩存;
ISP(互聯網服務提供商)的DNS服務器: ISP 有專門的 DNS 服務器應對 DNS 查詢請求;
根服務器: ISP 的 DNS 服務器還找不到的話,它就會向根服務器發出請求,進行遞歸查詢;
(DNS 服務器先問根域名服務器.com 域名服務器的 IP 地址,而後再問.github 域名服務器,依次類推)
  • 端口port
若是不是特別指定,http協議默認的80端口, https: 443

3.等待 TCP 隊列(瀏覽器對tcp的限制)

chorome瀏覽器有個機制, 統一域名下同時最多隻能創建 6 個 TCP鏈接(瀏覽器的限制可能不同), 若是超過 6 個請求, 那麼剩下的會進入排隊等待的狀態, 等待前面的完過後本身再去創建鏈接node

4.創建 TCP 鏈接

TCP 協議git

TCP運行: 鏈接建立、數據傳送和鏈接終止
  • 鏈接建立: 三次握手(保證可靠傳輸)
1.客戶端經過向服務器端發送一個SYN來建立一個主動打開,做爲三次握手的一部分;
客戶端把這段鏈接的序號設定爲隨機數A。

2.服務器端應當爲一個合法的SYN回送一個SYN/ACK。
ACK的確認碼應爲A+1,SYN/ACK包自己又有一個隨機產生的序號B。

3.最後,客戶端再發送一個ACK。此時包的序號被設定爲A+1,而ACK的確認碼則爲B+1。
當服務端收到這個ACK的時候,就完成了三次握手,並進入了鏈接建立狀態。

client側: SYN         ACK
server側:     SYN+ACK
  • 數據傳輸(一些機制)
使用序號,對收到的TCP報文段進行排序以及檢測重複的數據;
使用校驗和檢測報文段的錯誤,即無錯傳輸;
使用確認和計時器來檢測和糾正丟包或延時;
流量控制(Flow control);擁塞控制(Congestion control);丟失包的重傳。
  • 鏈接終止(四次揮手)
鏈接終止使用了四路握手過程(或稱四次握手,four-way handshake),在這個過程當中鏈接的每一側都獨立地被終止。
當一個端點要中止它這一側的鏈接,就向對側發送FIN,對側回覆ACK表示確認。
所以,拆掉一側的鏈接過程須要一對FIN和ACK,分別由兩側端點發出。

client側: FIN          ACK
server側:     ACK  FIN

5.發送 HTTP 請求

當 TCP 鏈接創建以後, 瀏覽器與服務器之間開始通訊,傳輸數據github

構建請求報文

  • 請求行
GET /appleguardu HTTP/1.1
// 請求方式:(Get、POST、OPTIONS、PUT、HEAD、DELETE、CONNECT、TRACE
// 資源路徑URI: /appleguardu
// 協議版本: HTTP 1.1
  • 請求頭(經常使用的)
Accept: 接收類型,表示瀏覽器支持的MIME類型(對標服務端返回的Content-Type)
Accept-Encoding: 瀏覽器支持的壓縮類型,如gzip等,超出類型不能接收
Content-Type: 客戶端發送出去實體內容的類型
Cache-Control: 指定請求和響應遵循的緩存機制,如no-cache
If-Modified-Since: 對應服務端的Last-Modified,用來匹配看文件是否變更,只能精確到1s以內,http1.0中
Expires: 緩存控制,在這個時間內不會請求,直接使用緩存,http1.0,並且是服務端時間
Max-age: 表明資源在本地緩存多少秒,有效時間內不會請求,而是使用緩存,http1.1中
If-None-Match: 對應服務端的ETag,用來匹配文件內容是否改變(很是精確),http1.1中
Cookie: 有cookie而且同域訪問時會自動帶上
Connection: 當瀏覽器與服務器通訊時對於長鏈接如何進行處理,如keep-alive
Host: 請求的服務器URL
Origin: 最初的請求是從哪裏發起的(只會精確到端口),Origin比Referer更尊重隱私
Referer: 該頁面的來源URL(適用於全部類型的請求,會精確到詳細頁面地址,csrf攔截經常使用到這個字段)
User-Agent: 用戶客戶端的一些必要信息,如UA頭部等
  • 請求體
post 請求中參數常放在請求體中
如參數的序列化形式(a=1&b=2這種;
或者直接放表單對象(Form Data對象,上傳時能夠夾雜參數以及文件的等

注意: 若是請求的是緩存資源,則會終止請求

瀏覽器會對請求的文件資源進行檢查, 若是這個請求的資源存在於瀏覽器緩存當中, 請求就會被攔截, 瀏覽器直接從本地緩存中獲取後發給網絡進程, 並結束此處請求過程
關於http緩存介紹web

緩存分類

緩存能夠簡單的劃分紅兩種類型: 強緩存(200 from cache)與協商緩存(服務端返回304);
強緩存: 瀏覽器若是判斷本地緩存未過時,就直接使用,無需發起http請求;
協商緩存304: 當瀏覽器再次向服務端發起http請求,而後服務端(304)告訴瀏覽器文件未改變,讓瀏覽器使用本地緩存apache

  • 如何區分兩類緩存? 經過設置 http 頭部信息
# 幾種頭部緩存屬性
強緩存(http1.1): Cache-Control/Max-Age 
強緩存(http1.0): Pragma/Expires 

協商緩存(http1.1): If-None-Match/E-tag
協商緩存(http1.0): If-Modified-Since/Last-Modified

<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
HTML頁面中也有一個meta標籤能夠控制緩存方案-Pragma,不過通常都是服務端設置
  • http1.0下的緩存控制
Pragma: 嚴格來講,它不屬於專門的緩存控制頭部,可是它設置no-cache時可讓本地強緩存失效(屬於編譯控制,來實現特定的指令,主要是由於兼容http1.0,因此之前又被大量應用)

Expires: 服務端配置的,屬於強緩存,用來控制在規定的時間以前,瀏覽器不會發出請求,而是直接使用本地緩存,
注意: Expires通常對應服務器端時間,如Expires: Fri, 30 Oct 1998 14:19:41
若是客戶端時間和服務端不一樣步,可能形成瀏覽器本地的緩存無用或者一直沒法過時

If-Modified-Since/Last-Modified: 這兩個是成對出現的,屬於協商緩存的內容;
其中瀏覽器的頭部是If-Modified-Since,而服務端的是Last-Modified;
它的做用是,在發起請求時,若是If-Modified-Since和Last-Modified匹配,
那麼表明服務器資源並未改變,所以服務端不會返回資源實體,而是隻返回頭部,通知瀏覽器可使用本地緩存...
Last-Modified,顧名思義,指的是文件最後的修改時間,並且只能精確到1s之內
注意: 若是服務端的文件會週期性的改變, 致使緩存失效
  • http1.1下的緩存控制
Cache-Control: 緩存控制頭部,有no-cache、max-age等多種取值

Max-Age: 服務端配置的,用來控制強緩存,在規定的時間以內,瀏覽器無需發出請求,直接使用本地緩存,
注意,Max-Age是Cache-Control頭部的值,不是獨立的頭部,
譬如Cache-Control: max-age=3600,並且它值得是絕對時間,由瀏覽器本身計算

If-None-Match/E-tag: 這兩個是成對出現的,屬於協商緩存的內容;
其中瀏覽器的頭部是If-None-Match,而服務端的是E-tag,
一樣,發出請求後,若是If-None-Match和E-tag匹配,則表明內容未變,通知瀏覽器使用本地緩存;
和Last-Modified不一樣,E-tag更精確,它是相似於指紋同樣的東西,因此會優先級也更高...
基於FileEtag INode Mtime Size生成,也就是說,只要文件變,指紋就會變,並且沒有1s精確度的限制。

6.服務器處理 http 請求

當服務器接收到瀏覽器的請求報文後, 會交給對應的處理程序進行處理(此處省略...),而後想服務器返回響應報文json

響應報文

  • 響應行
HTTP/1.1 200 OK
// 協議版本 狀態碼 狀態碼信息
// 200——代表該請求被成功地完成,所請求的資源發送回客戶端
// 304——自從上次請求後,請求的網頁未修改過,請客戶端使用本地緩存
// 400——客戶端請求有錯(譬如能夠是安全模塊攔截)
// 401——請求未經受權
// 403——禁止訪問(譬如能夠是未登陸時禁止)
// 404——資源未找到
// 500——服務器內部錯誤
// 503——服務不可用
  • 響應頭(經常使用的)
Access-Control-Allow-Headers: 服務器端容許的請求Headers
Access-Control-Allow-Methods: 服務器端容許的請求方法
Access-Control-Allow-Origin: 服務器端容許的請求Origin頭部(譬如爲*)
Content-Type: 服務端返回的實體內容的類型
Date: 數據從服務器發送的時間
Cache-Control: 告訴瀏覽器或其餘客戶,什麼環境能夠安全的緩存文檔
Last-Modified: 請求資源的最後修改時間
Expires: 應該在何時認爲文檔已通過期,從而再也不緩存它
Max-age: 客戶端的本地資源應該緩存多少秒,開啓了Cache-Control後有效
ETag: 請求變量的實體標籤的當前值
Set-Cookie: 設置和頁面關聯的cookie,服務器經過這個頭部把cookie傳給客戶端
Keep-Alive: 若是客戶端有keep-alive,服務端也會有響應(如timeout=38)
Server: 服務器的一些相關信息
  • 響應體
響應體通常是服務端須要傳給客戶端的內容;
如接口請求時: 實體中就是對應的信息的 json格式對象
如頁面請求時: 實體中就是對應的 html 字符串

注意

通常來講, 請求頭部和響應頭部是互相匹配的
如請求頭部的Accept要和響應頭部的Content-Type匹配,不然會報錯;
跨域請求時,請求頭部的Origin要匹配響應頭部的Access-Control-Allow-Origin,不然會報跨域錯誤
還有在使用緩存時,
請求頭部的If-Modified-Since、If-None-Match分別和
響應頭部的Last-Modified、ETag相對應跨域

以上基本都是經過服務端程序來控制的瀏覽器

重定向

當響應狀態碼是 301 時,會發生重定向, 瀏覽器進程從新讓導航進行工做, 修改 location 字段中的地址,繼續上述的請求流程

8.瀏覽器解析並渲染頁面

瀏覽器進程開始調度, 渲染進程經過管道接受網絡進程的html數據,開始解析html工做

渲染進程工做流

  • 1.DOM Tree 構建
當渲染進程接收到導航的確認信息,開始接受HTML數據時,主線程會解析文本字符串爲 DOM;
這裏依靠 HTMl 解析器: 
接受字節流 -> 維護 token 棧 -> 生成節點node -> 組成 DOM;

遇到內嵌 script 時, DOM解析工做中止; js引擎介入執行(可能會修改dom結構);
執行完 js 後恢復解析工做, 因此 js 會阻塞 dom 解析.

遇到其餘內聯資源時(css,img)會通知網絡進程去下載, 特別是 css;
js 在操做dom 樣式時會依賴cssom,生成 layoutTree也須要 cssom; 
因此 css 又會阻塞 js 的執行
  • 2.樣式計算, 構建cssom(css規則樹)
這裏會基於 CSS 選擇器解析 CSS 獲取每個節點的最終的計算樣式值;
對應的就是styleSheets
  • 3.計算佈局, 生成layout tree
想要渲染一個完整的頁面,除了獲知每一個節點的具體樣式,還須要獲知每個節點在頁面上的位置,
佈局實際上是找到全部元素的幾何關係的過程。

這裏經過遍歷 DOM 及相關元素的計算樣式,主線程會構建出包含每一個元素的座標信息及盒子大小的佈局樹。
佈局樹和 DOM 樹相似,可是其中只包含頁面可見的元素,若是一個元素設置了 `display:none` ,
這個元素不會出如今佈局樹上,僞元素雖然在 DOM 樹上不可見,可是在佈局樹上是可見的。
  • 4.分層,繪製(layer -> paint)
爲特定的節點生成專用圖層(will-change屬性), 生成 圖層樹;
爲圖層生成繪製表(記錄了繪製指令和順序), 提交到合成線程
  • 5.分塊,光柵化
合成線程將圖層分爲圖塊, 經過光柵化生成位圖(GPU 進程)
  • 6.合成,顯示
圖塊被光柵化後會生成一個繪製命令, 經過 IPC 提交給瀏覽器進程去執行,
繪製到內存中而後展現在顯示器上

9.斷開TCP鏈接

當數據傳送完畢,須要斷開 tcp 鏈接,此時發起 tcp 四次揮手;
一般狀況下,一旦服務器向客戶端返回了請求數據,它就要關閉 TCP 鏈接。
不過若是瀏覽器或者服務器在其頭信息中加入了

Connection: Keep-Alive (http/1.1下默認啓用, http/1.0默認close)
// keep-alive不會永遠保持,它有一個持續時間,通常在服務器中配置(如apache,
另外長鏈接須要客戶端和服務器都支持時纔有效

TCP 鏈接在發送後將仍然保持打開狀態,這樣瀏覽器就能夠繼續經過同一個 TCP 鏈接 發送請求。
保持 TCP 鏈接能夠省去下次請求時須要創建鏈接的時間,提高資源加載速度。 好比,一個 Web 頁面中內嵌的圖片就都來自同一個 Web 站點,若是初始化了一個持久連 接,你就能夠複用該鏈接,以請求其餘資源,而不須要從新再創建新的 TCP 鏈接。

參考

TCP 協議百科
HTTP headers

  • http2.0 瞭解
- 多路複用(即一個tcp/ip鏈接能夠請求多個資源)
- 首部壓縮(http頭部壓縮,減小體積)
- 二進制分幀(在應用層跟傳送層之間增長了一個二進制分幀層,改進傳輸性能,實現低延遲和高吞吐量)
- 服務器端推送(服務端能夠對客戶端的一個請求發出多個響應,能夠主動通知客戶端)
- 請求優先級(若是流被賦予了優先級,它就會基於這個優先級來處理,由服務器決定須要多少資源來處理該請求。)
相關文章
相關標籤/搜索