一文讀懂一個URL請求的過程是怎樣的

pjmike_pj發表於2018-08-27

前言

當我們在瀏覽器中輸入一個URL訪問地址,然後瀏覽器返回給我們一個響應頁面,這內部過程到底是怎樣的呢?下面我將從以下幾個方面闡述一個 WEB請求過程到底是怎樣:

  • 瀏覽器快取
  • DNS域名解析
  • TCP連線
  • HTTP請求與響應

瀏覽器的快取機制

這裡將瀏覽器機制放在第一步是考慮如果瀏覽器中有了快取資料,瀏覽器再次向目標URL傳送請求時,在資料不過期的情況下,會直接使用瀏覽器快取的資料,而不需要向服務端請求。下面的分析參考了徹底理解瀏覽器的快取機制的介紹,表示感謝。

瀏覽器和伺服器的通訊方式是應答方式,即 瀏覽器發起HTTP請求 - 伺服器響應該請求 。瀏覽器第一次向伺服器發起該請求後拿到請求結果,會根據響應報文中的HTTP頭的快取標識,決定是否快取結果,是則將請求結果和快取標識存入瀏覽器快取中,過程如下:

cache

  • 瀏覽器每次發起請求,都會先在瀏覽器快取中查詢該請求結果和快取標識
  • 瀏覽器每次拿到返回的請求結果都會將該結果和快取標識存入瀏覽器快取中

我們根據是否需要向伺服器重新發起HTTP請求將快取結果分為兩個部分,分別是強制快取(也叫本地快取)協商快取,當然這只是兩種叫法,有些人把快取分為Expires/Cache-controlLast-Modifed/Etag這兩種表現,本質上是一樣的。至於瀏覽器的快取到底是放在哪裡,我們後面揭曉

強制快取(也叫本地快取)

強制快取就是向瀏覽器快取查詢快取結果和快取標識,並根據該結果的快取規則來決定是否使用該快取結果的過程。強制快取又分為以下幾個方面:

  • 不存在該快取結果和快取標識,強制快取失敗,則直接向伺服器再一次傳送請求
    cache1.0
  • 存在請求結果和快取標識,但是該結果已失效過期,強制快取失敗,則使用協商快取(後續分析)
    cache1.1
  • 存在請求結果和快取標識,而且結果未過期,強制快取生效,直接返回結果
    cache1.2

強制快取的規則

當瀏覽器向伺服器傳送請求時,伺服器會將快取規則放入HTTP響應報文的HTTP頭中和請求結果一起返回給瀏覽器,控制強制快取的欄位分別是ExpiresCache-Control,其中Cache-ControlExpires優先順序高,兩個同時存在時,Cache-Control優先順序高

Expires
Expires: Wed, 21 Aug 2018 08:26:05 GMT
複製程式碼

Expires 是 HTTP/1.0控制網頁快取的欄位,其值為伺服器返回該請求結果快取的到期時間,這裡是絕對日期,如果客戶端的時間小於 Expires 的值時,直接使用快取結果

Expires是HTTP/1.0的欄位,現在瀏覽器預設使用HTTP/1.1,Expires已經被Cache-Control替代

Cache-Control

在HTTP/1.1中,Cache-Control主要用於控制網頁快取,其主要取值為:

  • public : 所有內容都被快取 (客戶端和代理伺服器都可快取)
  • private: 所有內容只有客戶端可以快取,Cache-Control的預設取值。
  • no-cache: 客戶端快取內容,但是是否使用快取則需要經過協商快取來驗證決定
  • no-store: 所有內容都不會被快取,即不使用強制快取,也不使用協商快取
  • max-age=xxx (xxx is numeric): 快取內容將在xxx秒後失效,這裡是相對值

協商快取

協商快取就是強制快取失效後,瀏覽器攜帶快取標識向伺服器發起請求,由伺服器根據快取標識決定是否繼續使用快取的過程。主要分為以下兩種情況:

第一種:協商快取生效,返回304

3o4

注: 304狀態碼錶示客戶端傳送附帶條件 (附帶條件包括If-Match,If-Modified-Since,If-None-Match等欄位) 的請求時,伺服器端允許請求訪問資源,但未滿足條件的情況。304狀態碼返回時,不包含任何響應的主體部分。

第二種:協商快取失效,返回200和請求結果結果

200

什麼導致協商快取生效或者失效

協商快取的控制欄位有 Last-Modified /If-Modified-Since 和 Etag/If-None-MatchEtag/If-None-Match的優先順序比Last-Modified /If-Modified-Since 高

  • Last-Modified

表示一個伺服器上的資源的最後修改時間,資源可以是靜態的或者動態的內容。一般在第一次請求時伺服器發給客戶端的快取標識

last-modified: Fri, 20 Apr 2018 10:10:50 GMT
複製程式碼
  • If-Modified-Since

客戶端再次傳送該請求時,攜帶上次請求返回的Last-Modified值,伺服器收到該請求,發現 If-Modified-Since欄位,如果伺服器資源的最後修改時間大於If-Modified-Since的值,則重新返回新資源,狀態碼為200,此時則表明協商快取失效,之前的快取不再使用。否則,返回304,資源沒有被更新,協商快取生效,繼續使用瀏覽器快取。

if-Modified-Since: Fri, 20 Apr 2018 10:10:50 GMT
複製程式碼
  • Etag

Etag是伺服器響應請求時,返回當前資原始檔的唯一的編號

  • If-None-Match

客戶端再次發起該請求時,攜帶上次請求返回的唯一編號,伺服器收到請求後,將If-None-Match的欄位值與該資源在伺服器上的Etag值做對比,一致則返回304,說明資源無更新,協商快取生效,繼續使用快取檔案,不一致說明資源已更新,重新返回資原始檔,狀態碼為200

瀏覽器快取

瀏覽器快取分為記憶體快取(from memory cache)硬碟快取(from disk cache):

  • 記憶體快取(from memory cache):具有兩個特點,分別是快速讀取時效性
    • 快速讀取: 記憶體快取會將編譯解析後的檔案,直接存入該程式的記憶體中,佔據該程式一定的內部資源,以方便下次執行使用時的快速讀取
    • 時效性:一旦程式關閉,則該程式的記憶體則會清空。
  • 硬碟快取(from disk cache): 硬碟快取則是直接將快取寫入硬碟檔案中,讀取快取需要對該快取存放的硬碟檔案進行I/O操作,然後重新解析該快取內容,讀取複雜,速度比記憶體快取慢。

使用者操作行為與快取

chach

  • 使用F5,瀏覽器將繞過本地快取(強制快取),但是協商快取還是有效的
  • 使用Ctrl + F5 強制重新整理,瀏覽器將繞過 本地快取和協商快取,讓伺服器返回最新的資源

瀏覽器快取機制小結

強制快取優先於協商快取執行,下面這張圖闡述了瀏覽器快取的流程:

cahce

注意: 當我們第一次訪問一個請求時,瀏覽器中是沒有快取的,這時候就需要到伺服器中去獲取,而與伺服器連線並獲取資源的這個過程,涉及到下面將的 DNS域名解析,TCP連線,HTTP請求等內容。這裡把瀏覽器快取機制放在第一位,是考慮如果客戶多次訪問同一個請求,是先去檢驗瀏覽器的快取的

DNS域名解析

我們知道網際網路都是通過URL來發布和請求資源的,而URL中的域名需要解析成IP地址才能與遠端主機建立連線,如何將域名解析成IP地址就屬於DNS解析的工作範疇。

DNS(Domain Name System)是域名系統的英文縮寫,是一種組織成域層次結構的計算機和網路服務命名系統,它用於TCP/IP網路,它所提供的服務是將主機名和域名轉換為IP地址的工作

分層式DNS域名伺服器

根據域名伺服器所起的作用,可以把域名伺服器分為以下幾種型別:

  • 根域名伺服器

根域名伺服器是最高層次的域名伺服器,也是最重要的域名伺服器。所有的根域名伺服器都知道所有的頂級域名伺服器的域名和IP地址。

  • 頂級域名伺服器(即TLD伺服器)

這些域名伺服器負載管理在該頂級域名伺服器註冊的所有二級域名。

  • 權威域名伺服器

例如:阿里萬網提供的dns9.hichina.com,dns10.hichina.com等,騰訊DNSPod提供的 f1g1ns1.dnspod.net,f1g1ns2.dnspod.net等,自己申請搭建的權威域名伺服器等

  • 本地域名伺服器

實際上本地域名伺服器不屬於域名伺服器的層次結構,但是它對域名系統非常重要,當一臺主機發出DNS查詢請求時,這個查詢請求報文首先是傳送給本地域名伺服器。每個ISP(當地網路接入商)都有一個本地域名伺服器

DNS域名解析過程

DNS域名解析的請求過程分為以下幾步:

第一步: 檢查瀏覽器快取中是否有這個域名對應的解析過的IP地址

瀏覽器會檢查快取中有沒有這個域名對應的解析過的IP地址,如果快取中有,這個解析過程就將結束。瀏覽器快取域名也是有限制的,不僅瀏覽器快取大小有限制,而且快取時間也有限制,通常情況下為幾分鐘到幾小時不等,域名被快取的時間限制可以通過TTL屬性來設定,時間要設定的合理。

第二步: 檢查作業系統快取中是否有這個域名對應的解析結果

如果使用者的瀏覽器快取沒有,瀏覽器會查詢作業系統快取中是否有這個域名對應的DNS解析結果。其實作業系統也會有一個域名解析的過程,在Windows中可以通過C:\Windows\System32\drivers\etc\hosts檔案來設定,在Linux中這個配置是 /etc/hosts。在這兩個檔案中,你可以將任何域名解析到任何能夠訪問的IP地址

第三步: 向本地伺服器查詢(LDNS)

如果前面兩個過程都無法解析,作業系統會把這個域名傳送給這裡設定的LDNS,也就是本地區的的域名伺服器。這個DNS通常都提供給你本地網際網路接入的一個DNS解析服務。比如你在學校接入網際網路,那麼本地DNS伺服器就在你的學校。實際上大約80%的域名服務解析都在這裡就完成了,LDNS承擔了主要的域名解析工作

第四步: LDNS沒有命中,直接到Root Server域名伺服器請求解析

根域名伺服器是最高層次的域名伺服器,也是最重要的域名伺服器。所有的根域名伺服器都知道所有的頂級域名伺服器的域名和IP地址

第五步:根域名伺服器返回給本地域名伺服器一個所查詢域的主域名伺服器(gTLD Server)地址

gTLD就是前面說的國際頂級域名伺服器,如.com,.cn等。

第六步: 本地域名伺服器再向上一步返回的gTLD伺服器傳送請求

第七步:接受請求的頂級域名伺服器查詢並返回此域名對應的權威伺服器(Name Server)

這個權威伺服器就是你註冊的域名伺服器,比如阿里萬網的域名伺服器,你這此申請的域名,那麼域名的解析任務就由這個域名提供商的伺服器來完成。

第八步:權威伺服器會查詢儲存的域名和IP的對映關係表並返回

正常情況下都根據域名得到目標IP記錄,連同一個TTL值返回給DNS Server域名伺服器。對於返回的IP和TTL,本地域名伺服器會快取這個域名和IP的對應關係,快取的時間由TTL值控制。

第九步:把解析的結果返回給使用者,使用者根據TTL值快取在本地系統快取中,域名解析過程結束

DNS域名解析過程中的查詢方式

  • 遞迴查詢

主機向本地域名伺服器的查詢一般都是採用遞迴查詢,所為遞迴查詢就是:如果主機所訪問的本地域名伺服器不知道被查詢域名的IP地址,那麼本地域名就以DNS客戶的身份,替代主機,向其他根域名伺服器繼續發出查詢請求報文,而不是讓主機自己進行下一步的查詢。

  • 迭代查詢

本地域名伺服器向根域名伺服器的查詢通常是採用迭代查詢。迭代查詢就是:當根域名伺服器收到本地域名伺服器發出的迭代查詢請求報文時,要麼給出所要查詢的IP地址,要麼告訴本地域名伺服器下一步應當向哪一個域名伺服器查詢,然後讓本地域名伺服器進行後續的查詢。

DNS中的快取

DNS域名解析過程後會快取解析結果,其中主要在兩個地方快取結果,一個是本地域名伺服器,一個是使用者的本地機器,這兩個快取都是TTL值和本機快取大小控制的。

DNS域名解析圖解過程

都說 「一圖勝千言」,下面這張圖具體闡述了DNS域名解析過程

dns

TCP連線

在瀏覽器傳送HTTP請求之前,需要在瀏覽器和伺服器之間建立一條TCP/IP連線。每一條TCP連線唯一地被通訊兩端的兩個端點(即兩個套接字)所確定,TCP連線的端點叫做套接字(socket)。根據RFC 793的定義,埠號拼接到IP地址即構成了套接字。而一次TCP連線主要分為建立連線(三次握手),資料傳輸,斷開連線(四次揮手),下圖給出了TCP的通訊過程

tcp_open_close

接下來的分析參考結合了《TCP/IP 詳解 卷1》和《計算機網路-謝希仁版》關於TCP的分析

TCP報文段首部中的幾個概念

tcp_header

在具體講TCP的三次握手和四次揮手之前,我們先來重點看下與TCP連線有關的報文段首部的幾個控制位與初始序列號,它將有助於我們後續的分析,至於有關TCP報文段首部更詳細的介紹請參閱《TCP/IP 詳解 卷1》或者相關文章。

確認號

確認號是期望收到對方下一個報文段的第一個資料位元組的序號。例如,B正確收到A傳送過來的一個報文段,其序號欄位值為501,而資料長度是200位元組(序號501 ~ 700),這表明B正確收到了A傳送的到序號為 700 為止的資料。所以,B期望收到A的下一個資料序號是 701 ,於是B將確認號置為 701,表示為 ACK = 701。總之,**若確認號 = N,則表明 N - 1 為止的所有資料都已正確收到

確認ACK

ACK標誌位用於確認收到

同步SYN

用來初始化一個連線的同步序列號,當建立一個新連線時,從客戶機傳送到伺服器的第一個報文段的SYN位欄位被啟用。

終止FIN

用來釋放一個連線,當FIN = 1時,表明此報文段的傳送方的資料已傳送完畢,並要求釋放運輸連線。

序列號

TCP是面向位元組流的,在一個TCP連線中傳送的位元組流中的每一個位元組都按順序編號,整個要傳送的位元組流的起始序號必須在連線建立時設定,這裡的起始序號就是TCP三次握手過程中的初始序列號(Initial Sequence Number,ISN)。在《TCP/IP詳解 卷1:協議》中談到,在一個連線中,TCP報文段在經過網路路後可能會存在延遲抵達與排序混亂的情況,為了解決這一問題,需要仔細選擇初始序列號,現代系統通常採用半隨機的方法選擇初始序列號,保證一定的安全性

這裡強調一點,確認號是針對接收方而言,接收方希望收到傳送方下一個報文段的第一個資料位元組的序號,而SYN所初始化的序列號是針對傳送方,在建立連線階段,傳送方和接收方都要傳送自己的初始序列號。畢竟TCP協議可以實現客戶端和服務端全雙工通訊。

tcp_open

第一次握手

客戶端在打算建立TCP連線時,向服務端發出 連線請求報文段(也就是SYN報文段,其SYN標誌被置位) 設定首部中的同步位 SYN = 1 ,同時隨機選擇一個初始序號 seq = x。TCP規定,SYN 報文段(即 SYN = 1 的報文段) 不能攜帶資料,但要消耗掉一個序號,這時,TCP客戶程式進入 SYN-SENT (同步已傳送) 狀態

第二次握手

伺服器收到連線請求報文段後,如同意建立連線,則向客戶端傳送確認,在確認報文段(SYN+ACK段)中應把 SYN 位和 ACK 位都置1,確認號是 ack = x + 1,表示服務端希望從客戶端這邊接收資料的序列號。同時也為自己隨機選擇一個初始序號 seq = y。請注意,這個報文段也不能攜帶資料,但同樣要消耗掉一個序號。這時 TCP 伺服器程式進入 SYN-RCVD(同步收到)狀態

第三次握手

客戶端收到伺服器傳送的確認後,還要再次向服務端給出確認。確認報文段的ACK置1,確認號 ack = y + 1.而自己的序號 seq = x + 1,TCP的標準規定,ACK報文段可以攜帶資料,但如果不攜帶資料則不訊息序號,在這種情況下,下一個資料包文段的序號仍是seq = x + 1。這是TCP連線已經建立,客戶端進入ESTABLISHED(已建立連線)狀態,服務端收到確認後,也進入 ESTABLISHED 狀態。

TCP連線為什麼是三次,而不是二次或者四次

知乎上對於這個問題點贊最多,而且比較通俗易懂的說法如下:

三次握手:
A:“喂,你聽得到嗎?”
B:“我聽得到呀,你聽得到我嗎?”
A:“我能聽到你,今天balabala……”

兩次握手:
A:“喂,你聽得到嗎?”
B:“我聽得到呀”
A:“喂喂,你聽得到嗎?”
B:“草,我聽得到呀!!!!”
A:“你TM能不能聽到我講話啊!!喂!”
B:“……”

四次握手:
A:“喂,你聽得到嗎?”
B:“我聽得到呀,你聽得到我嗎?”
A:“我能聽到你,你能聽到我嗎?”
B:“……不想跟傻逼說話”
複製程式碼

三次握手的目的是不僅在於通訊雙方瞭解一個連線正在建立,還在於利用資料包的選項來承載特殊的資訊,確保通訊雙方通道是可靠的,需要三次握手,更多關於此問題的討論參閱知乎連結: www.zhihu.com/question/24…

四次揮手

tcp_close

第一次揮手

客戶端向服務端發出連線釋放報文段(即FIN標誌置為1的報文段),並停止再傳送資料,客戶端把連線釋放的報文段首部的終止控制位FIN置1,其序號為 seq = u ,它等於前面已傳送過的資料的最後一個位元組的序號加1.這時客戶端進入FIN-WAIT-1 (終止等待 1)狀態,等待 B 的確認,請注意,TCP 規定,FIN 報文段即使不攜帶資料,它也消耗掉一個序號。

第二次揮手

伺服器端收到連線釋放報文段後即發出確認,確認號是 ack = u + 1,而這個報文段自己的序號是 v ,等於服務端前面已傳送過的資料的最後一個位元組的序號加1.然後服務端進入CLOSE-WAIT(關閉等待)狀態。這時的TCP連線處於 半關閉狀態,即客戶端已經沒有資料要傳送了,但服務端若傳送資料,客戶端仍要接收。服務端到客戶端這個方向的連線並沒有關閉,這個狀態可能要持續一段時間。

客戶端收到來自服務端的確認後,就進入 FIN-WAIT-2(終止等待 2) 狀態,等待 服務端發出的連線釋放報文段

第三次揮手

如果服務端已經沒有向客戶端傳送的資料了,其應用程式就通知服務端釋放連線。這時服務端發出的連線釋放報文段必須使 FIN = 1。現假定服務端的序號為 w (在半關閉狀態 B 可能又傳送了一些資料)。服務端還必須重複上次已經傳送過的確認號 ack = u + 1。這時 B 就進入 LAST-ACK (最後確認) 狀態,等待 A 的確認。

第四次揮手

客戶端收到服務端的連線釋放報文後,必須對此發出確認。在確認報文段中把 ACK 置為 1,確認號 ack = w + 1。而自己的序號是 seq = u + 1 (根據TCP標準,前面已經傳送過的 FIN 報文段要消耗一個序號)。然後進入TIME-WAIT(時間等待)狀態。這時,TCP連線還沒有釋放掉,等待時間計數器設定的時間 2MSL後,客戶端才進入到CLOSED狀態

為什麼是四次揮手

A: hi,我要關閉連線了
B: 好的,我收到了,我不接受你的資料了
B: hi,我也想關閉連線了
A: 好的,我不接收你的資料了。
複製程式碼

客戶端先向服務端傳送關閉請求,表示客戶端不再傳送資料了,服務端確認,但是,此時服務端還可以接著向客戶端傳送訊息,待服務端沒有資料傳送時,此時服務端也向客戶端傳送關閉請求,然後客戶端確認

TCP的半關閉

就像上面所說,TCP存在半關閉的狀態,雖然一些應用需要此功能,但它並不常見。TCP的半關閉就是指當一方已經完成了資料的傳送工作,併傳送了一個FIN給對方,但是它仍然希望接收來自對方的資料,直到對方傳送一個FIN給它,這種情況下它就處於TCP的半關閉狀態。

為什麼客戶端要在TIME-WAIT狀態必須等待2MSL的狀態

TIME_WAIT狀態也稱為 2MSL 等待狀態。在該狀態下,TCP 將會等待兩倍於最大段生存期(MSL)的時間,有時也被稱作加倍等待。這樣就能夠讓TCP重新傳送最終的ACK 以避免出現丟失的情況,重新傳送最終的 ACK 並不是因為TCP重傳了 ACK (它們不消耗序列號,也不會被TCP重傳),而是因為通訊另一方重傳了它的 FIN (它消耗一個序列號)

wireshell 簡要抓包分析

下面利用wireshell 簡要抓包分析,這裡只是簡單給出三次握手和四次揮手的過程,更多關於抓包的知識請參閱相關文章。以 IP地址 192.168.43.18754.190.14.101為例分析。

三次握手過程

three

四次揮手過程

four

HTTP請求與響應

瀏覽器與伺服器建立TCP連線以後,然後就可以開始發起HTTP請求了,客戶端按照指定格式開始向伺服器傳送HTTP請求,服務端接收請求後,解析HTTP請求,處理完業務邏輯,返回一個HTTP響應給客戶端,如下圖所示:

http

HTTP的請求報文與響應報文

HTTP的請求報文與響應報文結構一樣,都是分為報文首部和報文主體部分,請求報文中的主體部分主要傳送給服務端的資料,而響應報文中的主體則是客戶端需要的響應資料

header

而請求報文和響應報文的首部內容又由以下內容組成:

  • 請求行:包含用於請求的方法,請求URI和HTTP版本
  • 狀態行: 包含表明響應結果的狀態碼,原因短語和HTTP版本
  • 首部欄位: 包含表示請求和響應的各種條件和屬性的各類首部。
  • 其他: 包含HTTP的RFC裡未定義的首部(Cookie)等

header1

PS:更多關於HTTP首部欄位的內容請參閱 《圖解HTTP》

HTTP的常用請求方法

HTTP中用的比較多的請求方法主要包括:GET , POST,PUT,DELETE這四種

  • GET: 從指定的資源請求資料
  • POST: 用來傳輸實體的主體,建立資源
  • PUT: 用來建立或更新資源
  • DELETE: 用來刪除指定資源

返回結果的HTTP狀態碼

以下列舉一些常見的狀態碼:

  • 2XX成功
    • 200 OK客戶端請求被伺服器正常處理
    • 204 No Content :請求處理成功,但沒有資源可返回
  • 3XX重定向
    • 302Found 臨時重定向,請求的資源已被分配到新的URI。希望使用者能使用新的URI訪問。
    • 304Not Modified:客戶端傳送附帶條件的請求時,伺服器允許請求訪問資源,但因發生請求後未滿足條件的情況下,直接返回304,返回時不包含任何響應的主體部分。
  • 4XX客戶端錯誤
    • 400 Bad Request 請求報文存在語法錯誤。
    • 401 傳送的請求需要有通過HTTP的認證
    • 403 Forbidden 伺服器拒絕對請求資源的訪問
    • 404 Not Found 伺服器上無該資源。網頁不存在。
  • 5XX 伺服器錯誤
    • 500:伺服器內部出現錯誤
    • 503:伺服器暫時處於超負載或正在進行停機維護,現在無法處理請求。
    • 504:(閘道器超時)伺服器作為閘道器或代理,但是沒有及時從上游伺服器收到請求。

小結

上面闡述了瀏覽器中的URL請求過程,從 瀏覽器快取——>DNS域名解析——>TCP連線——>HTTP請求與響應 這4個階段進行了分析,一個web請求大致就分為這麼幾個過程,當然其中肯定有很多細節問題沒注意到,有問題歡迎指出。

參考資料 & 鳴謝

相關文章