HTTP請求流程詳解


HTTP協議是建立在TCP連接基礎之上的,它是一種允許瀏覽器向服務器發送請求獲取資源的協議,是Web的基礎。通常都是由瀏覽器發起請求,來獲取服務端的資源如HTML文件、CSS和js文件,除此之外還可以獲取圖片和視頻等資源,而作為前端開發,了解和瀏覽器打交道最廣的HTTP協議的請求流程,是十分重要的。

一、 瀏覽器發起HTTP請求的流程

1. 構建請求行

首先瀏覽器會基於請求構建請求行信息,請求行由三部分組成:
請求方法 請求的文件路徑 HTTP協議版本

GET index.html HTTP1.1

2. 查找瀏覽器緩存

在真正發起HTTP請求之前,瀏覽器會先在瀏覽器本地的緩存中查找是否有本次請求所需要的資源。

  • 如果發現在瀏覽器緩存中存有本次請求的資源副本,那么會攔截本次請求直接將緩存中文件返回,並直接結束本次HTTP請求,不會再去服務器重新請求。

  • 如果沒有在瀏覽器的緩存中查找到資源,那么才會進入網絡請求的過程。

3. 准備IP地址和端口號(在建立TCP連接之前)

由於瀏覽器這款軟件是將HTTP協議當作應用層的協議,用於封裝請求的文本信息;並且使用TCP/IP協議當作傳輸層協議,所以在發起HTTP請求之前必須先通服務器建立TCP連接,TCP協議是一種面向連接的、可靠的、基於字節流的傳輸協議,也就是說HTTP構建的請求行、請求頭這些數據必須通過TCP建立連接之后,在TCP的傳輸數據階段來實現傳輸的。

HTTP協議和TCP協議的關系大致是:

TCP建立連接階段
瀏覽器通服務器建立TCP連接,三次握手

TCP傳輸數據階段
瀏覽器發送HTTP請求,包括發送請求頭、請求行以及請求體
服務器接受到請求后開始處理HTTP請求...
服務端響應HTTP請求,包括返回響應行、響應頭以及響應體

TCP斷開連接階段
瀏覽器同服務器斷開TCP連接,四次揮手

問:HTTP請求的第一步是什么?
是准備同服務端建立TCP連接

問:建立TCP連接的第一步是什么?
准備目標端的IP地址和端口號,構建一個TCP頭信息附加到數據包的頭部。

問:IP地址和端口號從哪里獲取?

HTTP在發送請求前建立TCP連接時所需的IP地址和端口號從請求的URL中獲取。
一個合法的URL(統一資源定位符)中包含了請求的協議、域名、端口號、資源路徑、哈希、查詢參數等多個值。

  • 獲取到域名,基於DNS(Domain Name System 域名系統)服務器查詢到域名對應的主機IP地址。DNS查詢的過程中也會涉及到DNS預解析、瀏覽器提供的DNS域名緩存服務等操作,也是為了更快的確定服務端的IP地址。

  • 至於端口號,一般情況下如何URL中沒有指定端口號,那么HTTP協議默認是80端口,HTTPS協議默認為443端口。

4. 等待TCP隊列(在建立TCP連接之前)

如果是HTTP1.1版本的請求,在建立TCP連接之前還需要確認當前請求是否需要在建立TCP連接前排隊等候。
在Chrome中有一個機制,那就是同一個域名最多只能建立6個TCP連接,如果某一時刻同一域名下有10個請求發生,那么其余的4個會進入到TCP隊列中進行排隊等候,直到這個域名下進行中的域名請求完成之后,才可以進入TCP連接。

5. 建立TCP連接

此過程就是客戶端與服務端進行三次握手,也就是總共發送3個數據包確認連接的階段(后續會專門詳解三次握手的過程)

6. 發送HTTP請求

一旦建立TCP連接成功之后,就進入了數據傳輸階段。此時瀏覽器就可以和服務器進行通信了,而HTTP中的數據就是在這個通信過程中進行傳輸的。

瀏覽器會首先將構建好的請求行信息發送給服務器,請求行的作用就是告訴服務器客戶端所需要的資源是什么,請求行包含了請求的方法、請求的URI和HTTP版本協議

最常見的請求方法是GET和POST,GET請求一般不需要請求體,而是直接通過在請求URL中拼接查詢參數來告訴服務器。

POST請求一般用於客戶端提交一些信息如表單給服務器,一般POST請求還需要將收集的數據基於請求體發送給服務器。

在發送請求行之后瀏覽器會以請求頭的形式發送其他一些信息給服務器,請求頭的主要作用是瀏覽器的一些基礎信息告訴服務器,比如:

  • Host:當前請求的主機域名信息
  • Connection:keep-alive 告訴服務器保持長連接,避免重復三次握手建立tcp連接
  • Accept-Language:瀏覽器可以讀取的語言
  • Accept-Encodeing:瀏覽器可以支持的壓縮方式
  • Accept:瀏覽器支持的資源文件類型,如text/html
  • Cookie:瀏覽器端的Cookie信息
  • Cache-Control:瀏覽器端的緩存策略是強緩存
  • User-Agent:瀏覽器內核和操作系統等信息

二、 服務器響應HTTP請求的流程

服務器在接受到客戶端請求之后,就會基於請求行和請求頭中的信息來准備返回的資源文件。
基於curl -i命令可以查看服務器返回響應的全部數據
curl -i 會獲取響應頭和響應行以及響應體的全部數據
curl -I 只會獲取響應頭和響應行,不會獲取響應體數據

curl -i https://wht.im/

1. 返回請求

服務器會首先返回響應行信息給客戶端

HTTP/1.1 200 OK

響應行信息由三部分組成:HTTP協議版本 響應狀態碼 對於狀態碼的描述(關於響應狀態碼的分類會專門介紹),總之服務器會基於狀態碼來告訴瀏覽器本次請求的處理結果。

隨后服務器會返回響應頭信息給客戶端
服務器返回的響應頭信息中包含了服務器自身的一些信息:

Server: marco/2.14  服務器信息
Date: Sun, 28 Nov 2021 14:20:22 GMT 服務器生成返回數據的時間
Content-Type: text/html 返回的數據類型
Content-Length: 4340 返回的數據字節長度
Connection: keep-alive 保持TCP長連接
Vary: Accept-Encoding 
ETag: "484903b00f89708a70e3f085389563a8" 
Last-Modified: Thu, 22 Apr 2021 04:06:04 GMT
Expires: Sun, 28 Nov 2021 14:23:22 GMT
Cache-Control: public, must-revalidate, max-age=180
Accept-Ranges: bytes

最后服務器會返回響應體數據給客戶端,響應體中就包含了本次請求所需要的資源文件,瀏覽器會先從網絡進程去下載這個資源,然后將資源提交給渲染進程進行渲染。

2. 斷開連接

一般情況下:如果服務器向客戶端返回的資源,那么本次請求就到此結束,TCP連接就要關閉。
但是如果瀏覽器或者服務器的任意一方在其頭信息中加入了:Connection:keep-alive的話就代表TCP連接需要繼續保持連接狀態不要關閉,此時瀏覽器就可以復用這個TCP連接來傳輸數據,從而免去重新建立TCP連接所需的時間,從而提高資源加載的速度提升性能。

3. 重定向操作

一般情況下,一次完整的HTTP請求就到此結束了;但是如果服務器返回的狀態碼是301或者302的話,還涉及一個重定向操作。

如果服務端返回的狀態碼是301 Moved Permanently,也就是永久重定向,此時瀏覽器需要重定向到另外一個網址,網址信息就保存在響應頭的location字段中,並用該地址重新進行導航。

問題匯總

問題一:為什么有些站點第一次打開很慢,當再次訪問這個站點的時候速度很快?

這是因為在第一次請求的過程中,瀏覽器將請求的資源數據緩存在了本地。當第二次請求的時候,很多資源都不需要重新發起請求去獲取而是直接從本地緩存的資源副本加載,這中間省去了進行網絡請求所消耗的時間;除此之外,還有一個重要因素就是瀏覽器也會講DNS查詢記錄緩存在本地,也減少了DNS解析所消耗的時間。

服務器是通過什么方式讓瀏覽器緩存數據的?
第一次請求的時候,服務器會在HTTP的響應頭中通過Cache-Control字段來設置是否需要緩存該資源。
一般情況下,我們需要為這個資源設置緩存過期的時長,而這個時長就是通過Max-age值參數設置的,注意單位是s,不是ms。

Cache-Control:Max-age=1024;

代表着本次服務器返回的資源需要緩存,並且在瀏覽器本地緩存的時間是1024秒

  • 如果緩存資源沒有過去那么再次請求就會攔截該請求並直接返回緩存中的資源給瀏覽器
  • 如果緩存過期了,那么瀏覽器會繼續發起新的HTTP請求,並且在請求頭中加上:
If-None-Macth:'4f80f-13c-3a1xb12a';

服務器收到請求之后,會根據If-None-Macth字段的值來判斷本次請求的資源來服務器上是否有更新:

  • 如果沒有更新,那么就返回狀態碼304 Not Modified,告訴瀏覽器本次請求的資源在服務器沒有發生過更新,你可以繼續使用之前的緩存資源副本,這次就不再返回新的數據了。

  • 如果已經發生了更新,就將最新的資源返回給客戶端。

問題二:為什么登錄過一個網站之后,下次再訪問的時候就不需要重新登錄而已經是登錄態了?

  • 服務端在接受到瀏覽器提交的用戶登錄信息之后,查詢后台數據庫並驗證用戶身份是否正確,如果正確的話會生成一段表示用戶身份的字符串,並將該字符串參數寫入到響應頭的Set-Cookie字段中,然后將該信息響應給瀏覽器。
Set-Cookie:UID=159842301;
  • 瀏覽器接受到響應頭並解析,如果響應頭中包含了Set-Cookie字段,那么瀏覽器就會將該字段中的信息保存到本地,cookie是瀏覽器本地存儲的一種方案,有專門的js方法對其進行讀寫操作。

  • 當第二次用戶再次訪問該網站的時候,在發起請求之前瀏覽器會讀取之間存儲在本地的cookie信息,並把數據寫入到本次請求頭的Cookie字段中提交給服務器。

Cookie:UID=159842301;
  • 服務器在接受到請求之后,解析請求頭信息中的Cookie字段之后,獲取到UID=159842301的信息之后,服務器查詢數據庫來判斷該用戶是否為已登錄狀態,如果查詢正確就直接返回包含該用戶信息的數據給瀏覽器,就不用重新進行登錄了。

問題三:如果一個頁面的網絡加載時間過久,你是如何分析卡在哪個階段的?

最好的方法是基於Network面板下的Timing來來進行分析。
Timing表示該請求的時間線軸線:

  1. Quened at 36.01ms
    代表發送請求前的排隊所消耗時間為36.01ms
    有可能是前面有正在發送的請求
    有可能是有的請求優先級比較高所以排隊(network中有Priority選項代表請求的優先級)

    Started at 36.01ms
    正式開啟請求時間

  2. Connection Start 開始連接
    Stalled tcp鏈接復用 處理代理相關的邏輯所等待的時間
    DNS Lookup DNS域名解析消耗的時間
    Initial connection 創建TCP連接所消耗的時間
    SSL HTTPS請求進行SSL協商消耗的時間

  3. Request/Response 請求響應階段
    Request sent 發送請求的時間 很短 沒法優化
    Waiting(TTFB)從發起請求到服務器返回首字節時間(可優化點:請求發出到響應總時長)
    Content Download 服務器返回的資源下載所需時間(可優化點:返回資源的大小會影響時間)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM