1. 程式人生 > 其它 >一次完整的HTTP請求過程是怎麼樣的呢?【圖文詳解】(轉載)

一次完整的HTTP請求過程是怎麼樣的呢?【圖文詳解】(轉載)

前言

概述

過程詳解

一、DNS解析

二、TCP三次握手

三、 發起HTTP請求

四、伺服器響應HTTP請求

五、瀏覽器解析

六、瀏覽器進行頁面渲染

七、伺服器關閉TCP連線


前言

當我們在瀏覽器欄輸入:https://blog.csdn.net/dianxin113 的時候,具體發生了什麼呢?這個請求是怎麼到達伺服器及返回結果的呢?

概述

  1. 瀏覽器進行DNS域名解析,得到對應的IP地址

  2. 根據這個IP,找到對應的伺服器建立連線(三次握手)

  3. 建立TCP連線後發起HTTP請求(一個完整的http請求報文)

  4. 伺服器響應HTTP請求,瀏覽器得到html程式碼(伺服器如何響應)

  5. 瀏覽器解析html程式碼,並請求html程式碼中的資源(如js、css、圖片等)

  6. 瀏覽器對頁面進行渲染呈現給使用者

  7. 伺服器關閉TCP連線(四次揮手)

過程詳解

一、DNS解析

  1. 首先會搜尋瀏覽器自身的DNS快取(快取時間比較短,大概只有1分鐘,且只能容納1000條快取)

  2. 如果瀏覽器自身的快取裡面沒有找到,那麼瀏覽器會搜尋系統自身的DNS快取

  3. 如果還沒有找到,那麼嘗試從 hosts檔案裡面去找

  4. 在前面三個過程都沒獲取到的情況下,瀏覽器就會發起一個DNS的系統呼叫,就會向本地配置的首選DNS伺服器(一般是電信運營商提供的,也可以使用像Google提供的DNS伺服器)發起域名解析請求(通過的是UDP協議向DNS的53埠發起請求,這個請求是遞迴的請求,也就是運營商的DNS伺服器必須得提供給我們該域名的IP地址)

具體過程如下

DNS優化兩個方面:DNS快取、DNS負載均衡

二、TCP三次握手

三次握手完成之後這個TCP連線就進入Established狀態,就可以發起http請求了。

【問題1】:TCP 為什麼需要3次握手?

2個計算機通訊是靠協議(目前流行的TCP/IP協議)來實現,如果2個計算機使用的協議不一樣,那是不能進行通訊的,所以這個3次握手就相當於試探一下對方是否遵循TCP/IP協議,協商完成後就可以進行通訊了,當然這樣理解不是那麼準確。

【問題2】:為什麼HTTP協議要基於TCP來實現?

目前在Internet中所有的傳輸都是通過TCP/IP進行的,HTTP協議作為TCP/IP模型中應用層的協議也不例外,TCP是一個端到端的可靠的面向連線的協議,所以HTTP基於傳輸層TCP協議不用擔心資料的傳輸的各種問題。

三、 發起HTTP請求

HTTP是一個客戶端和伺服器端請求和應答的標準(TCP)。客戶端是終端使用者,伺服器端是網站。通過使用Web瀏覽器、網路爬蟲或者其它的工具,客戶端發起一個到伺服器上指定埠(預設埠為80)的HTTP請求。

通俗來講,他就是計算機通過網路進行通訊的規則,是一個基於請求與響應,無狀態的,應用層的協議,常基於TCP/IP協議傳輸資料。目前任何終端(手機,膝上型電腦。。)之間進行任何一種通訊都必須按照Http協議進行,否則無法連線。

一個HTTP請求報文由請求行(request line)、請求頭部(header)、空行和請求資料4個部分組成,下圖給出了請求報文的一般格式。

  • 請求行:用於描述客戶端的請求方式(GET/POST等),請求的資源名稱(URL)以及使用的HTTP協議的版本號。
    它們用空格分隔。例如,GET /index.html HTTP/1.1。
    HTTP協議的請求方法有GET、POST、HEAD、PUT、DELETE、OPTIONS、TRACE、CONNECT。

    1. GET是最常見的一種請求方式,當客戶端要從伺服器中讀取文件時,當點選網頁上的連結或者通過在瀏覽器的位址列輸入網址來瀏覽網頁的,使用的都是GET方式。GET方法要求伺服器將URL定位的資源放在響應報文的資料部分,回送給客戶端。
    2. POST方法可以允許客戶端給伺服器提供資訊較多。POST方法將請求引數封裝在HTTP請求資料中,以名稱/值的形式出現,可以傳輸大量資料,這樣POST方式對傳送的資料大小沒有限制,而且也不會顯示在URL中,對隱私資料保密性更好。
    3. HEAD就像GET,只不過服務端接受到HEAD請求後只返回響應頭,而不會發送響應內容。當我們只需要檢視某個頁面的狀態的時候,使用HEAD是非常高效的,因為在傳輸的過程中省去了頁面內容。
  • 請求頭:請求頭部由關鍵字/值對組成,每行一對,關鍵字和值用英文冒號“:”分隔。請求頭部通知伺服器有關於客戶端請求的資訊,典型的請求頭有:

    1. User-Agent:產生請求的瀏覽器型別。
    2. Accept:客戶端可識別的內容型別列表。
    3. Host:請求的主機名,允許多個域名同處一個IP地址,即虛擬主機。
    4. Connection告訴伺服器支援keep-alive特性
    5. Cookie每次請求時都會攜帶上Cookie以方便伺服器端識別是否是同一個客戶端
  • 空行:最後一個請求頭之後是一個空行,傳送回車符和換行符,通知伺服器以下不再有請求頭。

  • 請求資料:當使用POST等方法時,通常需要客戶端向伺服器傳遞資料。這些資料就儲存在請求正文中(GET方式是儲存在url地址後面,不會放到這裡)。POST方法適用於需要客戶填寫表單的場合。與請求資料相關的最常使用的請求頭是Content-Type和Content-Length。

【問題3】:那什麼是URL、URI、URN?

URI Uniform Resource Identifier 統一資源識別符號
URL Uniform Resource Locator 統一資源定位符
URN Uniform Resource Name 統一資源名稱

URL和URN 都屬於 URI,為了方便就把URL和URI暫時都通指一個東西

四、伺服器響應HTTP請求

接收到HTTP請求之後,就輪到負載均衡登場了,它位於網站的最前端,把短時間內較高的訪問量分攤到不同機器上處理。負載均衡方案有軟體、硬體兩種。軟體方案常見的就是NGINX了。

Nginx的作用主要有兩個1,處理靜態檔案請求,2轉發請求給後端伺服器。然後後端伺服器查詢資料庫返回資料。資料返回給客戶端仍然通過HTTP協議傳輸。

HTTP響應報文主要由狀態行、響應頭部、空行以及響應資料組成。

1.狀態行:由3部分組成,分別為:協議版本,狀態碼,狀態碼描述。

其中協議版本與請求報文一致,狀態碼描述是對狀態碼的簡單描述,所以這裡就只介紹狀態碼。

一些常見的狀態碼如下:

狀態程式碼為3位數字。

  1. 1xx:指示資訊–表示請求已接收,繼續處理。
  2. 2xx:成功–表示請求已被成功接收、理解、接受。
  3. 3xx:重定向–要完成請求必須進行更進一步的操作。
  4. 4xx:客戶端錯誤–請求有語法錯誤或請求無法實現。
  5. 5xx:伺服器端錯誤–伺服器未能實現合法的請求。

2.響應頭:響應頭用於描述伺服器的基本資訊,以及客戶端如何處理資料

3.空格:CRLF(即 \r\n)分割

4.訊息體:伺服器返回給客戶端的資料

上面的 HTTP 響應中,響應頭中的 Content-Length 同樣用於表示訊息體的位元組數。Content-Type 表示訊息體的型別,通常瀏覽網頁其型別是HTML,當然還會有其他型別,比如圖片、視訊等。

五、瀏覽器解析

瀏覽器拿到index.html檔案後,就開始解析其中的html程式碼,遇到js/css/image等靜態資源時,就向伺服器端去請求下載(會使用多執行緒下載,每個瀏覽器的執行緒數不一樣),這個時候就用上keep-alive特性了,建立一次HTTP連線,可以請求多個資源,下載資源的順序就是按照程式碼裡的順序,但是由於每個資源大小不一樣,而瀏覽器又多執行緒請求請求資源,所以從下圖看出,這裡顯示的順序並不一定是程式碼裡面的順序。

瀏覽器在請求靜態資源時(在未過期的情況下),向伺服器端發起一個http請求(詢問自從上一次修改時間到現在有沒有對資源進行修改),如果伺服器端返回304狀態碼(告訴瀏覽器伺服器端沒有修改),那麼瀏覽器會直接讀取本地的該資源的快取檔案。

六、瀏覽器進行頁面渲染

最後,瀏覽器利用自己內部的工作機制,把請求的靜態資源和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連線。

而關閉TCP連線就需要進行四次揮手了。

中斷連線端可以是客戶端,也可以是伺服器端。

  • 第一次揮手:客戶端傳送一個FIN=M,用來關閉客戶端到伺服器端的資料傳送,客戶端進入FIN_WAIT_1狀態。意思是說"我客戶端沒有資料要發給你了",但是如果你伺服器端還有資料沒有傳送完成,則不必急著關閉連線,可以繼續傳送資料。

  • 第二次揮手:伺服器端收到FIN後,先發送ack=M+1,告訴客戶端,你的請求我收到了,但是我還沒準備好,請繼續你等我的訊息。這個時候客戶端就進入FIN_WAIT_2 狀態,繼續等待伺服器端的FIN報文。

  • 第三次揮手:當伺服器端確定資料已傳送完成,則向客戶端傳送FIN=N報文,告訴客戶端,好了,我這邊資料發完了,準備好關閉連線了。伺服器端進入LAST_ACK狀態。

  • 第四次揮手:客戶端收到FIN=N報文後,就知道可以關閉連線了,但是他還是不相信網路,怕伺服器端不知道要關閉,所以傳送ack=N+1後進入TIME_WAIT狀態,如果Server端沒有收到ACK則可以重傳。伺服器端收到ACK後,就知道可以斷開連線了。客戶端等待了2MSL後依然沒有收到回覆,則證明伺服器端已正常關閉,那好,我客戶端也可以關閉連線了。最終完成了四次握手。

上面是一方主動關閉,另一方被動關閉的情況,實際中還會出現同時發起主動關閉的情況,也是通過四次握手。

【問題4】為什麼連線的時候是三次握手,關閉的時候卻是四次握手?

答:因為當Server端收到Client端的SYN連線請求報文後,可以直接傳送SYN+ACK報文。其中ACK報文是用來應答的,SYN報文是用來同步的。但是關閉連線時,當Server端收到FIN報文時,很可能並不會立即關閉SOCKET,所以只能先回復一個ACK報文,告訴Client端,"你發的FIN報文我收到了"。只有等到我Server端所有的報文都發送完了,我才能傳送FIN報文,因此不能一起傳送。故需要四步握手。

【問題5】為什麼TIME_WAIT狀態需要經過2MSL(最大報文段生存時間)才能返回到CLOSE狀態?

答:雖然按道理,四個報文都發送完畢,我們可以直接進入CLOSE狀態了,但是我們必須假象網路是不可靠的,有可以最後一個ACK丟失。所以TIME_WAIT狀態就是用來重發可能丟失的ACK報文。在Client傳送出最後的ACK回覆,但該ACK可能丟失。Server如果沒有收到ACK,將不斷重複傳送FIN片段。所以Client不能立即關閉,它必須確認Server接收到了該ACK。Client會在傳送出ACK之後進入到TIME_WAIT狀態。Client會設定一個計時器,等待2MSL的時間。如果在該時間內再次收到FIN,那麼Client會重發ACK並再次等待2MSL。所謂的2MSL是兩倍的MSL(Maximum Segment Lifetime)。MSL指一個片段在網路中最大的存活時間,2MSL就是一個傳送和一個回覆所需的最大時間。如果直到2MSL,Client都沒有再次收到FIN,那麼Client推斷ACK已經被成功接收,則結束TCP連線。

然而如果瀏覽器或者伺服器在其頭資訊加入了這行程式碼:

Connection:keep-alive

TCP連線在傳送後將仍然保持開啟狀態,於是,瀏覽器可以繼續通過相同的連線傳送請求。保持連線節省了為每個請求建立新連線所需的時間,還節約了網路頻寬。

自此一次完整的HTTP事務宣告完成。


轉載部落格: https://blog.csdn.net/dianxin113/article/details/104351670?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-0&spm=1001.2101.3001.4242 知道、想到、做到、得到