前言

無論大家是前端程序員還是後端程序員,相信大家對 HTTP 這個詞一定不陌生,在程序開發中它無處不在。所以,深入了解 HTTP 協議將會提高自己的編程質量以及提高前後端對接的效率。

HTTP 工作原理

HTTP 協議簡介

HTTP 協議是 Hyper Text Transfer Protocol(超文本傳輸協議)的縮寫,是用於從萬維網服務器傳輸超文本(例如 HTML 文件、圖片文件、視頻文件等)到本地瀏覽器的傳輸協議(應用層協議),也是互聯網上應用最為廣泛的一種網絡協議。

HTTP 架構設計

HTTP 是基於 TCP/IP 通信協議來傳遞數據,工作於客戶端-服務端架構上。瀏覽器作為 HTTP 客戶端通過 URL 向 HTTP 服務端發送請求,服務器收到請求後,向客戶端發送響應信息。瀏覽器通過與服務器建立 TCP 連接,之後發送 HTTP 請求與接收 HTTP 響應都是通過訪問 Socket 接口調用 TCP 協議實現。

(圖片來自網絡)

註意:在這種架構設計下,如果客戶端沒有向服務器發起請求,服務器無法主動向客戶端推送消息。

URL

HTTP 使用統一資源標識符(Uniform Resource Identifiers, URI)來傳輸數據和建立連接。一旦建立連接後,數據消息就通過類似 Internet 郵件所使用的格式[RFC5322]和多用途 Internet 郵件擴展(MIME)[RFC2045]來傳送。URL(Uniform Resource Locator)是一種特殊類型的 URI,包含了用於查找某個資源的足夠的信息。URL 中文叫統一資源定位符,是互聯網上用來標識某一處資源的地址。

http://www.munan.wiki:8080/tags/index.html?pageID=5&ID=25618&page=1#title 為例:

content 描述
http 該部分為協議部分,這代表該 URL 使用的是 HTTP 協議。
www.munan.wiki 該部分為域名部分。一個 URL 中,也可以使用 IP 地址作為域名。
8080 該部分為端口部分,域名和端口之間使用「:」作為分隔符。默認不寫為 80 端口。
tags 該部分為虛擬目錄部分,從域名後的第一個「/」開始到最後一個「/」為止,是虛擬目錄部分。
index.html 該部分為文件名部分,從域名後的最後一個「/」開始到「?」為止,是文件名部分。
pageID=5&ID=25618&page=1 該部分為參數部分。參數可以有多個,參數之間用「&」作為分隔符。
title 該部分為錨部分,從「#」開始到最後,都是錨部分。

HTTP 請求過程

平日裏訪問一個網站只需要在瀏覽器搜索框裏輸入網址也就是 URL 就可以了,從用戶輸入 URL 到看到網站內容經歷了以下幾個步驟:

(圖片來自網絡)

  1. 域名解析:首先會搜索本地 DNS 緩存,如果沒有就向 DNS 服務器發起域名解析,以獲取該域名所對應的 IP 地址;
  2. 建立 TCP 連接:獲取域名對應的 IP 後,跟據 IP 地址和端口號與服務器建立 TCP 連接(也就是 TCP 的 3 次握手連接);
  3. HTTP 請求:TCP 連接成功後,瀏覽器向服務器發起 HTTP 請求報文(報文內容包含請求行、請求頭部、請求主體,該請求報文作為 TCP 三次握手的第三個報文),用來向服務器請求文件;
  4. 服務器響應:服務器對瀏覽器請求作出響應,並把對應的 html 文本發送給瀏覽器;
  5. 解析代碼:瀏覽器得到響應後,首先解析狀態行,查看狀態碼表明請求是否成功,如果成功讀取響應數據並請求 html 文本中引用的圖片文件、js 文件、css 文件等;
  6. 渲染執行頁面:瀏覽器獲取所有資源後,對頁面進行渲染執行呈現給用戶。

HTTP 狀態碼

下面是常見的 HTTP 狀態碼(HTTP Status Code):

code 描述
1xx 信息響應類,服務器收到請求,需要請求者繼續執行操作。
100 (Continue)客戶端應繼續其請求。
101 (Switching Protocols)服務器根據客戶端的請求切換協議。只能切換到更高級的協議。
2xx 處理成功響應類,操作被成功接收並處理。
200 (OK)客戶端請求成功,一般用於 GET 與 POST 請求。
201 (Created)成功請求並創建了新的資源。
202 (Accepted)接受並處理、但處理未完成。
203 (Non-Authoritative Information)請求成功,但返回的 meta 信息不在原始的服務器,而是一個副本。
204 (No Content)服務器成功處理,但未返回內容。
205 (Reset Content)服務器處理成功,用戶終端應重置文檔視圖,可通過此返回碼清除瀏覽器的表單域(重置內容)。
206 (Partial Content)服務器成功處理了部分 GET 請求。
3xx 重定向響應類,需要進一步的操作以完成請求。
300 (Multiple Choices)請求的資源可包括多個位置,相應可返回一個資源特征與地址的列表用於用戶終端選擇。
301 (Moved Permanently)請求的資源已被永久的移動到新 URI,返回信息會包括新的 URI,瀏覽器會自動定向到新 URI。今後任何新的請求都應使用新的 URI 代替。
302 (Found)與 301 類似,但資源只是臨時被移動。客戶端應繼續使用原有 URI。
303 (See Other)與 301 類似,使用 GET 和 POST 請求查看,建議客戶訪問其他 URL 或訪問方式。
304 (Not Modified)所請求的資源未修改,服務器返回此狀態碼時,不會返回任何資源。客戶端通常會緩存訪問過的資源,通過提供一個頭信息指出客戶端希望只返回在指定日期之後修改的資源。
305 (Use Proxy)所請求的資源必須通過代理訪問。
306 (Unused)前一版本 HTTP 中使用的代碼,現行版本中不再使用,已經被廢棄的 HTTP 狀態碼。
307 (Temporary Redirect)臨時重定向,與 302 類似,使用 GET 請求重定向。
4xx 客戶端錯誤類,客戶請求包含語法錯誤或者是不能正確執行。
400 (Bad Request)客戶端請求有語法錯誤,不能被服務器所理解。
401 (Unauthorized)請求未經授權,這個狀態代碼必須和 WWW-Authenticate 報頭域一起使用。
402 (Payment Required)保留,將來使用。
403 (Forbidden)服務器理解請求客戶端的請求,但是拒絕提供服務。
404 (Not Found)服務器無法根據客戶端的請求找到資源(網頁)。
405 (Method Not Allowed)客戶端請求中的方法被禁止。
406 (Not Acceptable)根據發送的 Accept,請求資源不可訪問。
407 (Proxy Authentication Required)請求要求代理的身份認證,與 401 類似,但請求者應當使用代理服務器進行授權。
408 (Request Time-out)服務器等待客戶端發送的請求時間過長,超時。
409 (Conflict)服務器完成客戶端的 PUT 請求是可能返回此代碼,服務器處理請求時發生了沖突。
410 (Gone)客戶端請求的資源已經不存在。410 不同於 404,如果資源以前有現在被永久刪除了可使用 410 代碼。網站設計人員可通過 301 代碼指定資源的新位置。
411 (Length Required)服務器無法處理客戶端發送的不帶 Content-Length 的請求信息。
412(Precondition Failed) 一個或多個請求頭字段在當前請求中錯誤。
413 (Request Entity Too Large)請求的資源大於服務器允許的大小。由於請求的實體過大,服務器無法處理,因此拒絕請求。為防止客戶端的連續請求,服務器可能會關閉連接。如果只是服務器暫時無法處理,則會包含一個 Retry-After 的響應信息。
414 (Request-URI Too Large)請求的 URI 過長(URI 通常為網址),服務器無法處理。
415 (Unsupported Media Type)服務器無法處理請求附帶的媒體格式,請求資源不支持請求項目格式。
416 (Requested range not satisfiable)請求中包含 Range 請求頭字段,在當前請求資源範圍內沒有 range 指示值,請求也不包含 If-Range 請求頭字段。
417 (Expectation Failed)服務器不滿足請求 Expect 頭字段指定的期望值,如果是代理服務器,可能是下一級服務器不能滿足請求長。
5xx 服務端錯誤類,服務器在處理請求的過程中發生了錯誤。
500 (Internal Server Error)服務器內部錯誤,無法完成請求。
501 (Not Implemented)服務器不支持請求的功能,無法完成請求。
502 (Bad Gateway)作為網關或者代理工作的服務器嘗試執行請求時,從遠程服務器接收到了一個無效的響應。
503 (Server Unavailable)服務器當前不能處理客戶端的請求,一段時間後可能恢復正常。由於超載或系統維護,服務器暫時的無法處理客戶端的請求。延時的長度可包含在服務器的 Retry-After 頭信息中。
504 (Gateway Time-out)充當網關或代理的服務器,未及時從遠端服務器獲取請求。
505 (HTTP Version not supported)服務器不支持請求的 HTTP 協議的版本,無法完成處理。

HTTP 請求報文

HTTP 請求報文包含請求行、請求頭、空行、請求體。

(圖片來自網絡)

以請求 https://github.com/ 為例(下面為截圖):

請求行

綠色標記部分為請求行,表示請求方式為 GET 請求,使用 HTTP1.1 協議。GET 是 HTTP 的請求方式之一,HTTP1.1 協議中共定義了 8 種方法與服務器交互,有 GET、POST、HEAD、PUT、DELETE、OPTIONS、TRACE、CONNECT,比較常用的是 GET 和 POST 方法。

GET 請求方法後是 URL(這裏是「/」)和請求協議版本 1.1,之間用空格符隔開。

請求頭

HTTP 的頭域包括通用頭請求頭響應頭實體頭。請求頭是請求報文特有的,向服務器提交了一些額外的信息。

以下是常用字段所對應的作用:

Key Value 描述
Host www.github.com 客戶端指定自己將訪問的服務器域名(或者 IP 地址和端口號)。
Connection keep-alive 告訴服務器需要持久有效的連接狀態(HTTP1.1 默認會進行持久連接)。
Pragma no-cache 在 HTTP/1.1 協議中,Pragma:no-cache 的含義和 Cache-Control:no-cache 相同。
Cache-control no-cache 瀏覽器不使用緩存,強製向服務器請求資源。
If-Modified-Since Mon, 10 Apr 2019 22:08:55 GMT 請求的資源在指定日期以後,如果被更新就返回資源,否則返回「Not Modified」。
Upgrade-Insecure-Requests 1 表示瀏覽器可以處理 HTTPS 協議,並能把 HTTP 請求自動升級成 HTTPS。
Referer https://github.com/ 瀏覽器請求發出頁面所在地址,防盜鏈作用。
User-Agent Mozilla/5.0 (Windows NT 10.0; Win6… 表示用哪種瀏覽器發出的請求。
Accept text/html,application/xhtml+xml… 告訴服務器客戶端可接受 MIME 的類型。
Accept-Encoding gzip, deflate, br 表示客戶端支持壓縮內容編碼,去掉後會支持任意編碼。
Accept-Lanague zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7 告訴服務器客戶端能夠接受的語言,沒有則代表任何語言。
Cookies logged_in=no; _ga=GA1.2… 用於保持會話狀態,由服務器端設置,然後在後續請求中,供服務器讀取使用。

以下是詳細介紹 Cache-control 不同取值所代表的含義:

Value 描述
private 默認值。
max-age=3600 表示瀏覽器緩存相對於當前時刻 3600s 後過期,在這期間直接使用瀏覽器緩存。
max-age=0 表示每次請求都會訪問服務器通過 Last-Modified 來判斷文件是否被修改。如果被修改,返回狀態碼 200 並得到最新文件,否則返回狀態碼 304 並讀取緩存文件。
no-cache 瀏覽器不使用緩存,強製向服務器請求資源。
no-store 瀏覽器不會緩存所有內容。

空行

請求頭後面的空行是必須的。即使請求體數據為空,也必須有空行。

請求體

這裏存放請求時需要傳遞的數據。

GET 請求與 POST 請求的區別

  1. GET 請求提交的數據會附在 URL 之後(也就是把數據放置在請求頭中)以 ? 分割 URL,多個參數用 & 連接。如果數據是英文字母或者數字,則原樣發送;如果是空格,則轉換為 +;如果是中文或者其他字符,則直接把字符串用 BASE64 加密(如:%E4%BD%A0%E5%A5%BD,%XX 中的 XX 為該符號以 16 進製表示的 ASCII。)。POST 請求提交的數據會放置在請求體中。因此,GET 提交的數據會在地址欄中顯示出來。
  2. HTTP 協議沒有對傳輸的數據大小進行限製,HTTP 協議規範也沒有對 URL 長度進行限製。但在實際開發中,特定瀏覽器和服務器對 URL 長度有限製。例如 IE 對 URL 長度的限製是 2083 字節,其他瀏覽器如 Netscape、FireFox 等,理論上沒有長度限製,其限製取決於操作系統的支持。因此對於 GET 提交數據時,傳輸數據就會受到 URL 長度的限製。POST 請求由於不是通過 URL 傳遞數據,理論上數據不受限。但實際各種服務器會對 POST 提交的數據進行大小限製。
  3. POST 請求的安全性要比 GET 請求的安全性高。

HTTP 響應報文

一般情況下,服務器接收並處理客戶端發過來的請求後會返回一個 HTTP 的響應消息。HTTP 響應包含狀態行、響應頭、空行和響應體。

以請求 https://github.com/ 為例(下面為截圖):

狀態行

狀態行由 HTTP 協議版本號、狀態碼、狀態消息三部分組成(註意區分請求行)。此例中 HTTP 協議版本為 1.1 版本,狀態碼為 200,狀態消息為 OK。

響應頭

以下是常用字段所對應的作用:

Key Value 描述
Content-Encoding gzip 文檔的編碼(Encode)方法。只有在解碼之後才可以得到 Content-Type 頭指定的內容類型。利用 gzip 壓縮文檔能夠顯著地減少 HTML 文檔的下載時間。
Content-Length 數字 表示內容長度。只有當瀏覽器使用持久 HTTP 連接時才需要這個數據。
Content-Type text/plain 等 表示文檔屬於什麽 MIME 類型。
Date Mon, 10 Jun 2019 14:40:40 GMT 生成響應的日期和時間。
Expires 時間點 表示文檔什麽時候過期,從而不再使用該緩存。
Last-Modified 時間點 文檔的最後改動時間。瀏覽器通過 If-Modified-Since 為請求頭提供一個日期,該請求將被視為一個條件 GET,只有改動時間遲於指定時間的文檔才會返回,否則返回一個 304(Not Modified)狀態。
Server GitHub.com 服務器名字。
Set-Cookie _gh_sess=dm1tSXdxRExvSkpOZ… 設置和頁面關聯的 Cookie。
Refresh 表示瀏覽器多少時間之後刷新文檔,以秒計。
Location URL 地址 表示瀏覽器應當到哪裏去提取文檔(狀態代碼為 302)。

空行

響應頭後面的空行是必須的。即使響應體數據為空,也必須有空行。

響應體

服務器返回給客戶端的數據信息。

非持久連接與持久連接

在 HTTP 早期版本中,每進行一次通信就要斷開一次 TCP 連接,由於當時傳輸內容較小所以沒有什麽問題。然而現在不僅請求量較多而且每次請求數據較大(比如圖片網站),所以通信一次就斷開一次 TCP 連接的模式就不合適了。為了解決這個問題,HTTP1.1 協議出現了持久連接(HTTP Persistent Connections, HTTP keep-alive, HTTP connection reuse)功能。持久連接的特點是,只要任意一方沒有明確提出斷開連接則保持 TCP 連接狀態。在 HTTP1.1 協議中,所有連接默認是持久連接。

(圖片來自網絡)

總之,對於這些請求、響應,如果每次都經過一個單獨的 TCP 連接發送則稱為非持久連接。反之,如果每次都經過相同的 TCP 連接進行發送,則稱為持久連接。非持久連接在每次請求、響應之後都要斷開連接,下次再建立新的 TCP 連接。