Set-Cookie: SERVERID=1fa1f330efedec1559b3abbcb6e30f50|1587649463|1587649463;Path=/
Cache-Control:Max-age=2000
GET /index.html HTTP1.1
在瀏覽器實踐與原理(二)之TCP協議介紹了TCP協議是如何保證數據傳輸完成性的。
一個TCP鏈接包括了了建立連接、傳輸數據和斷開連接三個階段
HTTP協議是建立在TCP連接基礎之上的,HTTP是一種允許瀏覽器向服務器獲取資源的協議是 Web的基礎,通常由瀏覽器發起請求,用來獲取不同類型的文件,例如HTML、CSS、JS、圖片、文件、視頻等。
你是否有過下面這些疑問
- 為什么第一次打開一個站點速度很慢,當再次訪問這個站點速度就很快了
- 當登陸過一個網址之后,下次再訪問該站點就已經處於登錄狀態了
這是怎么做到的呢?在今天這篇文章中,將通過分析一個HTTP請求過程中的每一步狀態來了解完整的HTTP請求
瀏覽器端發起HTTP請求流程
如果你在瀏覽器地址欄里輸入極客時間網站的地址:www.taobao.com
接下來瀏覽器會完成哪些動作呢?一步一步來追蹤一下
1.構建請求
首先,瀏覽器構建請求行信息(如下所示),構建好后,瀏覽器准備發起網絡請求
GET /index.html HTTP1.1
2.查找緩存
在真正發起網絡請求前,瀏覽器會現在瀏覽器緩存中查詢是否有要請求的文件。其中,瀏覽器緩存是一種在本地保存資源富文本,以供下次請求時直接使用的技術
當瀏覽器發現請求的資源已經在瀏覽器緩存中存有副本,它會攔截請求,返回該資源的副本,並直接結束請求,而不會去資源服務器重新下載。這的好處:
- 緩解服務器壓力,提升性能(獲取資源的耗時更短了)
- 對於網站來說,緩存是實現快速資源加載的重要組成部分
當然查找失敗會進入網絡請求
3.准備IP地址和端口
在了解網絡請求之前,我們需要先看看HTTP和TCP的關系。
因為瀏覽器使用HTTP作為應用層協議,用來封裝請求的文本信息;並使用TCP/IP作傳輸協議將它發送到網絡上,所以在HTTP開始工作之前,瀏覽器需要通過TCP與服務器建立連接,也就是說HTTP的內容是通過TCP的傳輸數據階段來實現的
看上圖,你可以思考這么一連串問題
- HTTP網絡請求的第一步是做什么呢?結合上圖看是服務器和TCP鏈接
- 那建立連接的信息都有了嗎?在瀏覽器實踐與原理(二)之TCP協議說到建立TCP連接的第一步需要准備IP和端口號
- 那么怎么獲取IP和端口號呢?這得看看我們現在有什么,我們有一個URL地址,是否可以利用URL地址來獲取IP和端口信息呢
數據包是通過IP地址傳輸給接收方的,由於IP地址是數字標識如:39.106.233.176,難以記憶,但使用域名(www.xxx.com)就方便多了,所以基於這個需求又出現了一個服務,
負責把域名和IP地址做一一映射關系,這套域名映射為IP的系統就叫做域名系統,簡稱DNS
所以,你會發現第一步瀏覽器會請求DNS返回域名對應的IP。瀏覽器還提供了DNS數據緩存服務,如果某個域名已經解析過了,瀏覽器會緩存解析結果,供下次使用
拿到IP之后,接下來基因獲取端口號了,如果URL沒有特別指明端口號,那么HTTP協議默認是80端口。
4.等待TCP隊列
准備好端口和IP地址,下一步就是建立TCP鏈接了嗎?
答案是不闊以哦,Chrome有個機制,同一個域名同時最多只能建立6個TCP連接,如果在同一個域名下同時有10個請求發生,那么其中四個請求會進入排隊等待狀態,直至請求完成
如果請求數小於6,建立TCP連接
5.建立TCP連接
排隊結束后,可以快樂的和服務器握手了,在HTTP工作之前,瀏覽器先通過TCP建立連接
6.發送HTTP請求
一旦建立了連接,瀏覽器就可以和服務器進行通信,而HTTP中的數據正是在這個通信過程中傳輸
可以結合下圖理解瀏覽器如何發送請求信息給服務器的
- 首先,瀏覽器會像服務器發送請求行,它包括了請求方法、請求URL和HTTP版本協議,發送請求行,就是告訴瀏覽器需要什么資源,最常用的是Get。比如,直接在瀏覽器地址欄輸入極客時間的域名(https://time.geekbang.org/)就告訴瀏覽器要Get首頁資源。
- 另外一個常用的請求方法是POST,它用於發送一些數據給服務器,比如一個登陸網站,使用post方法,瀏覽器要准備數據給服務器,這里准備的數據通過請求體發送
- 在瀏覽器發送請求命令之后,還要以請求頭形式發送一些其他信息,把瀏覽器的一些基礎信息告訴服務器,包括了瀏覽器的操作系統、瀏覽器內核等信息,以及當前請求的域名信息,瀏覽器的cookie等。
服務器處理HTTP請求流程
歷經千辛萬苦,HTTP的請求信息終於被送達了服務器,接下來,服務器會根據瀏覽器的請求信息准備相應內容
1.返回請求
curl -i https://time.geekbang.org/
這里加上了 i 是為了返回響應行、響應頭和響應體的數據,返回的結果如下圖:
首先,服務器會返回響應行,包括協議版本和狀態碼
但不是所有的請求都會被服務器成功處理的,那么一些無法處理或者處理出錯的信息,怎么辦呢?
服務器會通過響應行的狀態碼來告訴瀏覽器它的處理結果,比如
- 最常用的狀態碼是200,表示處理成功
- 如果沒有找到頁面返回404
隨后,服務器也會隨同響應向瀏覽器發送響應頭。響應頭包含了服務器自身的一些信息,比如服務器生成返回數據的時間、返回的數據類型,以及服務器在客戶端保存的Cookie等信息。
發送完響應頭后,服務器可以繼續發送響應體的數據
2.斷開連接
通常情況下,一旦服務器向客戶端返回了請求數據,就關閉TCP連接,不過瀏覽器或者服務器在其頭信息加入了
Connection: keep-alive
那么TCP連接在發送后仍保持打開狀態,這樣瀏覽器就可以繼續通過同一個TCP連接發送請求。
保持TCP連接可以省去下次請求時需要建立連接的時間,提升資源加載速度
比如同一個Web頁面中內嵌的圖片都來自同一個Web站點,如果初始化了一個持久連接,就可以復用該連接,以請求其他資源。
3.重定向
到這里似乎請求快結束了,不過還有一只能情況需要了解一下,比如你在瀏覽器中打開geekbang.org后,會發現最終打開的地址是https://www.geekbang.org/
這兩個URL之所以不一樣,是因為涉及到了一個重定向操作,在控制台輸入
curl -I geekbang.org
注意這里輸入的參數是-I,和-i不一樣,-I是只需要獲取響應頭和響應數據,不需要獲取響應體數據
- 從上圖可以看到,響應行返回的狀態碼是301,狀態301是告訴瀏覽器,我需要重定向到另外一個地址
- 需要重定向的網址正是包含在響應頭的Location字段中的地址,並使用改地址重新導航
注:不過也不要認為這種跳轉是必然的,如果你打開 https://12306.cn會發現這個站點是打不開的,因為12306的服務器並沒有處理跳轉操作所以需要輸入完整的https://www.12306.cn/才能打開頁面
問題解答
為什么很多站點打開第二次速度會很快?
如果打開第二次速度會很快,主要原因是第一次加載過程中,緩存了一些耗時的數據
哪些數據會被緩存呢?
從上面介紹請求路徑看, DNS緩存和頁面緩存資源這兩塊數據是會被瀏覽器緩存的
看下瀏覽器資源緩存,如下圖
首先,來看下服務器是通過什么方式讓瀏覽器緩存數據的?
從上圖可以看出第一次請求可以看出,當服務器返回HTTP響應頭給瀏覽器時,瀏覽器通過響應頭中的Cache-Control字段來設置一個緩存的過期時長,而這個時長是通過Max-age參數來設置的,如上圖設置緩存過期時間是2秒
Cache-Control:Max-age=2000
這也就意味着,在該緩存資源還未過期的情況下,如果再次請求資源,會直接返回緩存中的資源給瀏覽器
但如果資源過期了,瀏覽器會繼續發起網絡請求,並且在HTTP請求頭帶上
If-None-Match:"4f80f-13c3alxb12a"
服務器收到請求后,會根據If-None-Match的值來判斷請求的資源是否有更新
- 如果沒有更新,就返回304狀態碼,相當於告訴瀏覽器:“z這個緩存可以繼續使用,這次就不重復發送數據給你了”
- 如果資源有更新,服務器就直接返回最新資源給瀏覽器
簡單來說,瀏覽器第二次訪問能夠秒開,是因為這些網站把很多資源都緩存在了本地,瀏覽器緩存直接使用本地副本回應請求,不會產生真正的網絡請求
2.登錄狀態是如何保持的?
- 用戶打開登錄界面,在登錄框填入用戶名和密碼,點擊登錄按鈕,調用post提交登錄信息給服務端
- 服務器收到瀏覽器提交的信息之后,查詢后台,驗證用戶信息是否正確,如果正確,會生成一段表示用戶身份的字符串,並把該字符串寫到響應頭的Set-cookie字段,發送給瀏覽器
- 瀏覽器在接受到服務器響應后,開始解析響應頭,如果遇到響應頭含義Set-Cookie字段的情況,瀏覽器會把這個字段信息保存到本地
- 當用戶再次訪問時瀏覽器會發起HTTP請求,但在發起請求之前,瀏覽器會之前保存的Cookie數據,並把數據寫到請求頭里的Cookie字段里
- 服務器在收到HTTP請求頭數據之后,就會查找包含用戶Cookie的信息,然后查詢后台,判斷用戶已是登錄狀態,生成有該用戶的頁面數據,發送給瀏覽器
- 瀏覽器在接收到該含有當前用戶的頁面數據后,就可以正確展示登錄狀態信息了
Set-Cookie: SERVERID=1fa1f330efedec1559b3abbcb6e30f50|1587649463|1587649463;Path=/
通過上面這個流程可以知道瀏覽器頁面狀態是通過Cookie實現的
如圖
簡單地說,如果服務器端發送的響應頭內有 Set-Cookie 的字段,那么瀏覽器就會將該字段的內容保持到本地。當下次客戶端再往該服務器發送請求時,客戶端會自動在請求頭中加入 Cookie 值后再發送出去。服務器端發現客戶端發送過來的 Cookie 后,會去檢查究竟是從哪一個客戶端發來的連接請求,然后對比服務器上的記錄,最后得到該用戶的狀態信息。
下圖幫你理解 HTTP請求示意圖