一個完整的HTTP請求過程

一個完整的HTTP請求過程

整個流程

域名解析 —> 與服務器建立連接 —> 發起HTTP請求 —> 服務器響應HTTP請求,瀏覽器得到html代碼 —> 瀏覽器解析html代碼,並請求html代碼中的資源(如js、css、圖片) —> 瀏覽器對頁面進行渲染呈現給用戶

1. 域名解析

以Chrome瀏覽器爲例:

① Chrome瀏覽器 會首先搜索瀏覽器自身的DNS緩存(緩存時間比較短,大概只有1分鐘,且只能容納1000條緩存),看自身的緩存中是否有https://www.cnblogs.com 對應的條目,而且沒有過期,如果有且沒有過期則解析到此結束。

注:我們怎麼查看Chrome自身的緩存?可以使用 chrome://net-internals/#dns 來進行查看

② 如果瀏覽器自身的緩存裏面沒有找到對應的條目,那麼Chrome會搜索操作系統自身的DNS緩存,如果找到且沒有過期則停止搜索解析到此結束.

注:怎麼查看操作系統自身的DNS緩存,以Windows系統爲例,可以在命令行下使用 ipconfig /displaydns 來進行查看

③ 如果在Windows系統的DNS緩存也沒有找到,那麼嘗試讀取hosts文件(位於C:\Windows\System32\drivers\etc),看看這裏面有沒有該域名對應的IP地址,如果有則解析成功。

④ 如果在hosts文件中也沒有找到對應的條目,瀏覽器就會發起一個DNS的系統調用,就會向本地配置的首選DNS服務器(一般是電信運營商提供的,也可以使用像Google提供的DNS服務器)發起域名解析請求(通過的是UDP協議向DNS的53端口發起請求,這個請求是遞歸的請求,也就是運營商的DNS服務器必須得提供給我們該域名的IP地址),運營商的DNS服務器首先查找自身的緩存,找到對應的條目,且沒有過期,則解析成功。如果沒有找到對應的條目,則有運營商的DNS代我們的瀏覽器發起迭代DNS解析請求,它首先是會找根域的DNS的IP地址(這個DNS服務器都內置13臺根域的DNS的IP地址),找打根域的DNS地址,就會向其發起請求(請問www.cnblogs.com這個域名的IP地址是多少啊?),根域發現這是一個頂級域com域的一個域名,於是就告訴運營商的DNS我不知道這個域名的IP地址,但是我知道com域的IP地址,你去找它去,於是運營商的DNS就得到了com域的IP地址,又向com域的IP地址發起了請求(請問www.cnblogs.com這個域名的IP地址是多少?),com域這臺服務器告訴運營商的DNS我不知道www.cnblogs.com這個域名的IP地址,但是我知道cnblogs.com這個域的DNS地址,你去找它去,於是運營商的DNS又向cnblogs.com這個域名的DNS地址(這個一般就是由域名註冊商提供的,像萬網,新網等)發起請求(請問www.cnblogs.com這個域名的IP地址是多少?),這個時候cnblogs.com域的DNS服務器一查,誒,果真在我這裏,於是就把找到的結果發送給運營商的DNS服務器,這個時候運營商的DNS服務器就拿到了www.cnblogs.com這個域名對應的IP地址,並返回給Windows系統內核,內核又把結果返回給瀏覽器,終於瀏覽器拿到了www.cnblogs.com 對應的IP地址,該進行一步的動作了。

注:一般情況下是不會進行以下步驟的

如果經過以上的4個步驟,還沒有解析成功,那麼會進行如下步驟(以下是針對Windows操作系統):

⑤ 操作系統就會查找NetBIOS name Cache(NetBIOS名稱緩存,就存在客戶端電腦中的),那這個緩存有什麼東西呢?凡是最近一段時間內和我成功通訊的計算機的計算機名和Ip地址,就都會存在這個緩存裏面。什麼情況下該步能解析成功呢?就是該名稱正好是幾分鐘前和我成功通信過,那麼這一步就可以成功解析。

⑥ 如果第⑤步也沒有成功,那會查詢WINS 服務器(是NETBIOS名稱和IP地址對應的服務器)

⑦ 如果第⑥步也沒有查詢成功,那麼客戶端就要進行廣播查找

⑧ 如果第⑦步也沒有成功,那麼客戶端就讀取LMHOSTS文件(和HOSTS文件同一個目錄下,寫法也一樣)

如果第八步還沒有解析成功,那麼就宣告這次解析失敗,那就無法跟目標計算機進行通信。只要這八步中有一步可以解析成功,那就可以成功和目標計算機進行通信。

2. 與服務器建立連接

2.1 TCP連接的建立

客戶端的請求到達服務器,首先就是建立TCP連接

Client首先發送一個連接試探,ACK=0 表示確認號無效,SYN = 1 表示這是一個連接請求或連接接受報文,同時表示這個數據報不能攜帶數據,seq = x 表示Client自己的初始序號(seq = 0 就代表這是第0號包),這時候Client進入syn_sent狀態,表示客戶端等待服務器的回覆

Server監聽到連接請求報文後,如同意建立連接,則向Client發送確認。TCP報文首部中的SYN 和 ACK都置1 ,ack = x + 1表示期望收到對方下一個報文段的第一個數據字節序號是x+1,同時表明x爲止的所有數據都已正確收到(ack=1其實是ack=0+1,也就是期望客戶端的第1個包),seq = y 表示Server 自己的初始序號(seq=0就代表這是服務器這邊發出的第0號包)。這時服務器進入syn_rcvd,表示服務器已經收到Client的連接請求,等待client的確認。

Client收到確認後還需再次發送確認,同時攜帶要發送給Server的數據。ACK 置1 表示確認號ack= y + 1 有效(代表期望收到服務器的第1個包),Client自己的序號seq= x + 1(表示這就是我的第1個包,相對於第0個包來說的),一旦收到Client的確認之後,這個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協議不用擔心數據的傳輸的各種問題。

2.2 常見TCP連接限制

2.2.1 修改用戶進程可打開文件數限制

在Linux平臺上,無論編寫客戶端程序還是服務端程序,在進行高併發TCP連接處理時,最高的併發數量都要受到系統對用戶單一進程同時可打開文件數量的限制(這是因爲系統爲每個TCP連接都要創建一個socket句柄,每個socket句柄同時也是一個文件句柄)。可使用ulimit命令查看系統允許當前用戶進程打開的文件數限制,windows上是256,linux是1024,這個博客的服務器是65535

2.2.2 修改網絡內核對TCP連接的有關限制

在Linux上編寫支持高併發TCP連接的客戶端通訊處理程序時,有時會發現儘管已經解除了系統對用戶同時打開文件數的限制,但仍會出現併發TCP連接數增加到一定數量時,再也無法成功建立新的TCP連接的現象。出現這種現在的原因有多種。 

第一種原因可能是因爲Linux網絡內核對本地端口號範圍有限制。此時,進一步分析爲什麼無法建立TCP連接,會發現問題出在connect()調用返回失敗,查看系統錯誤提示消息是“Can’t assign requestedaddress”。同時,如果在此時用tcpdump工具監視網絡,會發現根本沒有TCP連接時客戶端發SYN包的網絡流量。這些情況說明問題在於本地Linux系統內核中有限制。

其實,問題的根本原因在於Linux內核的TCP/IP協議實現模塊對系統中所有的客戶端TCP連接對應的本地端口號的範圍進行了限制(例如,內核限制本地端口號的範圍爲1024~32768之間)。當系統中某一時刻同時存在太多的TCP客戶端連接時,由於每個TCP客戶端連接都要佔用一個唯一的本地端口號(此端口號在系統的本地端口號範圍限制中),如果現有的TCP客戶端連接已將所有的本地端口號佔滿,則此時就無法爲新的TCP客戶端連接分配一個本地端口號了,因此係統會在這種情況下在connect()調用中返回失敗,並將錯誤提示消息設爲“Can’t assignrequested address”。

2.3 TCP四次揮手

當客戶端和服務器通過三次握手建立了TCP連接以後,當數據傳送完畢,肯定是要斷開TCP連接的啊。那對於TCP的斷開連接,這裏就有了神祕的“四次分手”。

第一次分手:主機1(可以使客戶端,也可以是服務器端),設置Sequence Number,向主機2發送一個FIN報文段;此時,主機1進入FIN_WAIT_1狀態;這表示主機1沒有數據要發送給主機2了;

第二次分手:主機2收到了主機1發送的FIN報文段,向主機1回一個ACK報文段,Acknowledgment Number爲Sequence Number加1;主機1進入FIN_WAIT_2狀態;主機2告訴主機1,我“同意”你的關閉請求;

第三次分手:主機2向主機1發送FIN報文段,請求關閉連接,同時主機2進入LAST_ACK狀態;

第四次分手:主機1收到主機2發送的FIN報文段,向主機2發送ACK報文段,然後主機1進入TIME_WAIT狀態;主機2收到主機1的ACK報文段以後,就關閉連接;此時,主機1等待2MSL後依然沒有收到回覆,則證明Server端已正常關閉,那好,主機1也可以關閉連接了。

問題1:爲什麼要四次分手?

TCP協議是一種面向連接的、可靠的、基於字節流的運輸層通信協議。TCP是全雙工模式,這就意味着,當主機1發出FIN報文段時,只是表示主機1已經沒有數據要發送了,主機1告訴主機2,它的數據已經全部發送完畢了;但是,這個時候主機1還是可以接受來自主機2的數據;當主機2返回ACK報文段時,表示它已經知道主機1沒有數據發送了,但是主機2還是可以發送數據到主機1的;當主機2也發送了FIN報文段時,這個時候就表示主機2也沒有數據要發送了,就會告訴主機1,我也沒有數據要發送了,之後彼此就會愉快的中斷這次TCP連接。

3. 發起HTTP請求

3.1 HTTP協議

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

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

3.1.1 四個基於

請求與響應:客戶端發送請求,服務器端響應數據

無狀態的:協議對於事務處理沒有記憶能力,客戶端第一次與服務器建立連接發送請求時需要進行一系列的安全認證匹配等,因此增加頁面等待時間,當客戶端向服務器端發送請求,服務器端響應完畢後,兩者斷開連接,也不保存連接狀態,一刀兩斷!恩斷義絕!從此路人!下一次客戶端向同樣的服務器發送請求時,由於他們之前已經遺忘了彼此,所以需要重新建立連接。

應用層: Http是屬於應用層的協議,配合TCP/IP使用。

TCP/IP: Http使用TCP作爲它的支撐運輸協議。HTTP客戶機發起一個與服務器的TCP連接,一旦連接建立,瀏覽器(客戶機)和服務器進程就可以通過套接字接口訪問TCP。

3.2 HTTP請求報文

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

3.2.1 請求行

請求行分爲三個部分:請求方法、請求地址和協議版本

請求方法

HTTP/1.1 定義的請求方法有8種:GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS、TRACE。

最常的兩種GET和POST,如果是RESTful接口的話一般會用到GET、POST、DELETE、PUT。

請求地址

URL:統一資源定位符,是一種自願位置的抽象唯一識別方法。

組成:<協議>://<主機>:<端口>/<路徑> 注:端口和路徑有時可以省略(HTTP默認端口號是80)

https://localhost:8080/index.html?key1=value1&keys2=value2

協議版本

協議版本的格式爲:HTTP/主版本號.次版本號,常用的有HTTP/1.0和HTTP/1.1

3.2.2 請求頭部

請求頭部爲請求報文添加了一些附加信息,由“名/值”對組成,每行一對,名和值之間使用冒號分隔。

常見請求頭如下:

請求頭部的最後會有一個空行,表示請求頭部結束,接下來爲請求數據,這一行非常重要,必不可少。

3.2.3 請求數據

可選部分,比如GET請求就沒有請求數據。

下面是一個POST方法的請求報文:

POST  /index.php HTTP/1.1    請求行

Host: localhost

User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:10.0.2) Gecko/20100101 Firefox/10.0.2  請求頭

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8

Accept-Language: zh-cn,zh;q=0.5

Accept-Encoding: gzip, deflate

Connection: keep-alive

Referer: http://localhost/

Content-Length:25

Content-Type:application/x-www-form-urlencoded

  空行

username=aa&password=1234  請求數據

4. 服務器響應HTTP請求,瀏覽器得到html代碼

4.1 負載均衡

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

4.1.1 負載均衡硬件方案

F5 BIG-IP是著名的硬件方案,但這裏不作討論

4.1.2 負載均衡軟件方案

有LVS HAProxy Nginx等,留作以後補充

在典型的Rails應用部署方案中,Nginx的作用有兩個

處理靜態文件請求

轉發請求給後端的Rails應用 

這是一個簡單的Nginx配置文件

後端的Rails服務器通過unix socket與Nginx通信,Nginx伺服public文件夾裏的靜態文件給用戶

待完善…

4.2 Rails(應用服務器)

4.3 數據庫(數據庫服務器)

4.4 Redis、Memercache(緩存服務器)

4.5 消息隊列

4.6 搜索

4.7 HTTP響應報文

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

4.7.1 狀態行

由3部分組成,分別爲:協議版本,狀態碼,狀態碼描述。

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

狀態碼

狀態代碼爲3位數字。

1xx:指示信息–表示請求已接收,繼續處理。

2xx:成功–表示請求已被成功接收、理解、接受。

3xx:重定向–要完成請求必須進行更進一步的操作。

4xx:客戶端錯誤–請求有語法錯誤或請求無法實現。

5xx:服務器端錯誤–服務器未能實現合法的請求。

下面列舉幾個常見的:

4.7.2 響應頭部

與請求頭部類似,爲響應報文添加了一些附加信息

常見響應頭部如下:

4.7.3 響應數據

用於存放需要返回給客戶端的數據信息。

下面是一個響應報文的實例:

HTTP/1.1 200 OK  狀態行

Date: Sun, 17 Mar 2013 08:12:54 GMT  響應頭部

Server: Apache/2.2.8 (Win32) PHP/5.2.5

X-Powered-By: PHP/5.2.5

Set-Cookie: PHPSESSID=c0huq7pdkmm5gg6osoe3mgjmm3; path=/

Expires: Thu, 19 Nov 1981 08:52:00 GMT

Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0

Pragma: no-cache

Content-Length: 4393

Keep-Alive: timeout=5, max=100

Connection: Keep-Alive

Content-Type: text/html; charset=utf-8

  空行

  響應數據

HTTP響應示例

Hello HTTP!

關於請求頭部和響應頭部的知識點很多,這裏只是簡單介紹。

5. 瀏覽器解析html代碼,並請求html代碼中的資源

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

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

詳細的瀏覽器工作原理請看:http://kb.cnblogs.com/page/129756/

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

最後,瀏覽器利用自己內部的工作機制,把請求到的靜態資源和html代碼進行渲染,渲染之後呈現給用戶。

參考文獻

一次完整的HTTP請求與響應涉及了哪些知識? https://blog.csdn.net/LRH0211/article/details/72724361

一次完整的HTTP請求過程 https://www.cnblogs.com/engeng/articles/5959335.html

後端知識體系–一次完整的HTTP請求 https://blog.csdn.net/u012248450/article/details/51036329

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章