
最近看了http相關的知識點,覺得還是有必要整理下,這樣對自己的網路知識體系也有幫助,
HTTP 是什么
http叫超文本傳輸協議,可以拆成超文本、傳輸、協議來理解協議
http 是一個用在計算機里面的協議,使用計算機通信之間的一個規范,以及相關各種控制和錯誤處理方式,簡單的說就是約定、規則,
傳輸
http 是一個在計算機世界里專門用來在計算機之間傳輸資料的約定和規范,
超文本
所謂“超文本”,就是“超越了普通文本的文本”,它是文字、圖片、音頻和視頻等的混合體,最關鍵的是含有“超鏈接”,能夠從一個“超文本”跳躍到另一個“超文本”,形成復雜的非線性、網狀的結構關系
http 不是一個孤立的協議
http 是一個應用層協議, 通常跑在 TCP/IP 協議堆疊之上,依靠 IP 協議實作尋址和路由、TCP 協議實作可靠資料傳輸、DNS 協議實作域名查找、SSL/TLS 協議實作安全通信,此外,還有一些協議依賴于 HTTP,例如 WebSocket、HTTPDNS 等,
HTTP 優缺點
特點
靈活可擴展
語法上只規定了基本格式,空格分隔單詞,換行分隔欄位等,以及傳輸上不僅可以傳輸文本,還可以傳輸圖片,視頻等任意資料,
可靠傳輸
因為 HTTP 協議是基于 TCP/IP 的,而 TCP 本身是一個“可靠”的傳輸協議,所以 HTTP 自然也就繼承了這個特性,能夠在請求方和應答方之間“可靠”地傳輸資料,請求-應答模式
請求 - 應答模式是 HTTP 協議最根本的通信模型,簡單的理解就是,一方發訊息,一方接收訊息
無狀態
整個協議里沒有規定任何的“狀態”,客戶端和服務器永遠是處在一種“無知”的狀態,建立連接前兩者互不知情,每次收發的報文也都是互相獨立的,沒有任何的聯系,
缺點
不安全
明文傳輸,其次HTTP 協議也不支持“完整性校驗”,資料在傳輸程序中容易被竄改而無法驗證真偽,為了解決 HTTP 不安全的缺點,所以就出現了 HTTPS
性能
http 是請求-應答模式,發起的請求類似一個佇列,先進先出,會存在對頭阻塞問題
無狀態
這個無狀態主要看應用場景,對于不需要背景關系狀態的,這個就是優點,對于需要記錄狀態的,這個就是缺點,當然這個可以通過其他方式來做,比如 cookie,token 等
HTTP 報文結構
HTTP 協議的請求報文和回應報文的結構基本相同,由三大部分組成:
- 起始行(start line):描述請求或回應的基本資訊
- 頭部欄位集合(header):使用 key-value 形式更詳細地說明報文
- 訊息正文(entity):實際傳輸的資料,它不一定是純文本,可以是圖片、視頻等二進制資料

前兩部分起始行和頭部欄位經常又合稱為“請求頭”或“回應頭”,訊息正文又稱為“物體”,但與“header”對應,很多時候就直接稱為“body”,
HTTP 協議規定報文必須有 header,但可以沒有 body,而且在 header 之后必須要有一個“空行”(區分body 與 head 分割),也就是CRLF,

HTTP 頭欄位非常靈活,不僅可以使用標準里的 Host、Connection 等已有頭,也可以任意添加自定義頭,這就給 HTTP 協議帶來了非常好的靈活、擴展性,
請求行
報文里的起始行也就是請求行(request line),它簡要地描述了客戶端想要如何操作服務器端的資源,

狀態行
這里它不叫“回應行”,而是叫“狀態行”(status line),意思是服務器回應的狀態,

頭部欄位
請求行或狀態行再加上頭部欄位集合就構成了 HTTP 報文里完整的請求頭或回應頭 如圖:
HTTP 常見狀態碼
RFC 規定 HTTP 的狀態碼為「三位數」,第一個數字定義了回應的類別,被分為五類:
- 1xx: 代表請求已被接受,需要繼續處理,
- 2xx: 表示成功狀態,
- 3xx: 重定向狀態,
- 4xx: 客戶端錯誤,
- 5xx: 服務器端錯誤,
客戶端作為請求的發起方,獲取回應報文后,需要通過狀態碼知道請求是否被正確處理,是否要再次發送請求,如果出錯了原因又是什么,這樣才能進行下一步的動作,要么發送新請求,要么改正錯誤重發請求,
服務器端作為請求的接收方,也應該很好地運用狀態碼,在處理請求時,選擇最恰當的狀態碼回復客戶端,告知客戶端處理的結果,指示客戶端下一步應該如何行動,特別是在出錯的時候,盡量不要簡單地返 400、500 這樣意思含糊不清的狀態碼,
目前 RFC 標準里總共有 41 個狀態碼,但狀態碼的定義是開放的,允許自行擴展,所以 Apache、Nginx 等 Web 服務器都定義了一些專有的狀態碼,如果你自己開發 Web 應用,也完全可以在不沖突的前提下定義新的代碼,
1xx 資訊類
接受的請求正在處理,資訊類狀態碼,
2xx 成功
- 200 OK 表示從客戶端發來的請求在服務器端被正確請求,
- 204 No content,表示請求成功,但沒有資源可回傳,
- 206 Partial Content,該狀態碼表示客戶端進行了范圍請求,而服務器成功執行了這部分的 GET 請求 回應報文中包含由 「Content-Range」 指定范圍的物體內容,
3xx 重定向
- 301 moved permanently,永久性重定向,表示資源已被分配了新的 URL,這時應該按 Location 首部欄位提示的 URI 重新保存,
- 302 found,臨時性重定向,表示資源臨時被分配了新的 URL,
- 304 資源未修改,拿快取的資源

301 和 302 都會在回應頭里使用欄位 Location 指明后續要跳轉的 URI,最終的效果很相似,瀏覽器都會重定向到新的 URI,兩者的根本區別在于語意,一個是“永久”,一個是“臨時”,
比如,你的網站升級到了 HTTPS,原來的 HTTP 不打算用了,這就是“永久”的,所以要配置 301 跳轉,把所有的 HTTP 流量都切換到 HTTPS,
304 Not Modified 是一個比較有意思的狀態碼,它用于 If-Modified-Since 等條件請求,表示資源未修改,用于快取控制,它不具有通常的跳轉含義,但可以理解成“重定向已到快取的檔案”(即“快取重定向”),
4xx 客戶端錯誤
- 400 bad request,通用的錯誤碼,請求報文存在語法錯誤,
- 403 forbidden,表示對請求資源的訪問被服務器拒絕,服務器經常訪問資源,
- 404 not found,表示在服務器上沒有找到請求的資源,
- 405 Method Not Allowed,服務器禁止使用該方法,客戶端可以通過options方法來查看服務器允許的訪問方法
5xx 服務的錯誤
- 500 internal sever error,表示服務器端在執行請求時發生了錯誤,
- 502 Bad Gateway,服務器自身是正常的,訪問的時候出了問題,具體啥錯誤我們不知道,
- 503 service unavailable,表明服務器暫時處于超負載或正在停機維護,無法處理請求,
HTTP主要版本之間的差異
http0.9
20 世紀 90 年代初期的互聯網世界非常簡陋,計算機處理能力低,存盤容量小,網速很慢,這一時期的 HTTP 被定義為 0.9 版,結構比較簡單,只允許用“GET”動作從服務器上獲取 HTML 檔案,并且在回應請求之后立即關閉連接,功能非常有限,該版本已經過時,http1.0
相比0.9版本,1.0版本增加了很多功能,例如:
- 傳輸的資料不再僅限于文本,影像、視頻、音頻都能傳輸
- 引入了 HTTP Header(頭部)的概念,讓 HTTP 處理請求和回應更加靈活
- 增加了回應狀態碼,標記可能的錯誤原因
- 增加了 HEAD、POST 等新方法
http1.1
http1.1是對 http1.0 的小幅度修改,但一個重要的區別是:它是一個“正式的標準”
- 明確了連接管理,允許持久連接,即TCP建立連接之后默認不關閉,可以被多個請求復用,不用宣告Connection: keep-alive
- 增加了 PUT、DELETE 等新的方法
- 增加了快取管理和控制,如增加了 E-tag,If-Unmodified-Since, If-Match, If-None-Match 等快取控制標頭來控制快取失效
- 支持斷點續傳,通過使用請求頭中的
Range來實作, - 允許回應資料分塊(chunked)傳輸,有利于傳輸大檔案
- 強制要求 請求頭帶上Host 頭,讓互聯網主機托管成為可能,
其中http1.1 版本是目前主流版本,
http2
隨著互聯網在飛速發展,目前http1.0 不符合現在的網路環境要求了,所以谷歌在1.0 的基礎上提出了SPDY 協議,優化了1.0,http/2 的制定充分考慮了現今互聯網的現狀:寬帶、移動、不安全,在高度兼容 http/1.1 的同時在性能改善方面做了很大努力,
主要的特點有:
- 二進制協議,不再是純文本,包括頭部欄位、body 的物體資料都是二進制,并且統稱為"幀":頭資訊幀和資料幀
- 可發起多個請求,廢棄了 1.1 里的管道
- 增加流、二進制分幀的概念
- 多路復用TCP連接,在一個連接里,客戶端和瀏覽器都可以同時發送多個請求或回應,且不用按順序一一對應,這樣子解決了http隊頭阻塞的問題,
- 使用專用演算法(HPACK)壓縮頭部,減少資料傳輸量
- 允許服務器主動向客戶端推送資料
http3
在http2 的基礎之上又進一步優化,出現了QUIC 協議,后面更名為http/3,HTTP/3 目前正式進入了標準化制訂階段,
TCP/IP 協議堆疊
tcp/ip 是一個有層次的協議堆疊,TCP/IP 協議總共有四層,就像搭積木一樣,每一層需要下層的支撐,同時又支撐著上層,任何一層被抽掉都可能會導致整個協議堆疊坍塌,每一層只做自己的事情,然后把結果給其他層,這樣的結構責任清晰,便于擴展,
第一層:鏈接層(資料鏈路層)負責在以太網、WiFi 這樣的底層網路上發送原始資料包,作業在網卡這個層次,使用 MAC 地址來標記網路上的設備,所以有時候也叫 MAC 層,
第二層:“網際層”或者“網路互連層”(internet layer)也叫網路層,IP 協議就處在這一層,因為 IP 協議定義了“IP 地址”的概念,所以就可以在“鏈接層”的基礎上,用 IP 地址取代 MAC 地址,把許許多多的局域網、廣域網連接成一個虛擬的巨大網路,在這個網路里找設備時只要把 IP 地址再“翻譯”成 MAC 地址就可以了,
第三層:“傳輸層”(transport layer),這個層次協議的職責是保證資料在 IP 地址標記的兩點之間“可靠”地傳輸,是 TCP 協議作業的層次,另外還有它的一個“小伙伴”UDP,
第四層叫“應用層”(application layer),由于下面的三層把基礎打得非常好,所以在這一層就“百花齊放”了,有各種面向具體應用的協議,例如 Telnet、SSH、FTP、SMTP 等等,當然還有我們的 HTTP,
TCP/IP作業方式
HTTP 協議的傳輸程序就是通過協議堆疊逐層向下,每一層都添加本層的專有資料,層層打包,然后通過下層發送出去,
接收資料則是相反的操作,從下往上穿過協議堆疊,逐層拆包,每層去掉本層的專有頭,上層就會拿到自己的資料,
簡單的理解就是類似發快遞的程序,快遞員從你那拿到包裹,拿到地址,裝到他的車子,運送到站點,在分類打包,送去各個物流中轉站,到達之后,對方再依次拆包,拿到你寄送過去的物品,

TCP/IP四層與OSI七層模型
TCP/IP 發明于 1970 年代,當時除了它還有很多其他的網路協議,整個網路世界比較混亂,
這個時候國際標準組織(ISO)注意到了這種現象,就想要來個“大一統”,于是設計出了一個新的網路分層模型,想用這個新框架來統一既存的各種網路協議,TCP/IP 等協議已經在許多網路上實際運行,再推翻重來是不可能的,
所以,OSI 分層模型在發布的時候就明確地表明是一個“參考”,不是強制標準

第一層:物理層,TCP/IP 里無對應;
第二層:資料鏈路層,對應 TCP/IP 的鏈接層;
第三層:網路層,對應 TCP/IP 的網際層;
第四層:傳輸層,對應 TCP/IP 的傳輸層;
第五、六、七層:統一對應到 TCP/IP 的應用層,
HTTP 連接管理
目前主流版本http1.1 中的連接管理是會存在對頭阻塞的問題,所以性能是存在一點問題的,這個后面在說,
短連接
HTTP 協議最初(0.9/1.0)是個非常簡單的協議,通信程序也采用了簡單的“請求 - 應答”方式,
它底層的資料傳輸基于 TCP/IP,每次發送請求前需要先與服務器建立連接,收到回應報文后會立即關閉連接,
因為客戶端與服務器的整個連接程序很短暫,不會與服務器保持長時間的連接狀態,所以就被稱為“短連接”(short-lived connections),早期的 HTTP 協議也被稱為是“無連接”的協議,
短連接的缺點相當嚴重,因為在 TCP 協議里,建立連接和關閉連接都是非常“昂貴”的操作,TCP 建立連接要有“三次握手”,發送 3 個資料包;關閉連接是“四次揮手”,4 個資料包需要 ,
長連接
針對短連接暴露出的缺點,HTTP 協議就提出了“長連接”的通信方式,也叫“持久連接”,
其實解決辦法也很簡單,用的就是“成本均攤”的思路,既然 TCP 的連接和關閉非常耗時間,那么就把這個時間成本由原來的一個“請求 - 應答”均攤到多個“請求 - 應答”上,

連接相關的頭部欄位
由于長連接對性能的改善效果非常顯著,所以在 HTTP/1.1 中的連接都會默認啟用長連接,不需要用什么特殊的頭欄位指定,只要向服務器發送了第一次請求,后續的請求都會重復利用第一次打開的 TCP 連接,也就是長連接,在這個連接上收發資料,
也可以在請求頭里明確地要求使用長連接機制,使用的欄位是 Connection,值是“keep-alive”,如圖:

長連接缺點
因為 TCP 連接長時間不關閉,服務器必須在記憶體里保存它的狀態,這就占用了服務器的資源,如果有大量的空閑長連接只連不發,就會很快耗盡服務器的資源,導致服務器無法為真正有需要的用戶提供服務,
所以,長連接也需要在恰當的時間關閉,不能永遠保持與服務器的連接,這在客戶端或者服務器都可以做到,
在客戶端,可以在請求頭里加上“Connection: close”欄位,告訴服務器:“這次通信后就關閉連接”,服務器看到這個欄位,就知道客戶端要主動關閉連接,于是在回應報文里也加上這個欄位,發送之后就呼叫 Socket API 關閉 TCP 連接,
服務器端通常不會主動關閉連接,但也可以使用一些策略,拿 Nginx 來舉例,它有兩種方式:
- 使用keepalive_timeout指令,設定長連接的超時時間,如果在一段時間內連接上沒有任何資料收發就主動斷開連接,避免空閑連接占用系統資源,
- 使用keepalive_requests指令,設定長連接上可發送的最大請求次數,比如設定成 1000,那么當 Nginx 在這個連接上處理了 1000 個請求后,也會主動斷開連接,
HTTP 快取控制
http 的快取是通過Cache-Control 欄位來控制
- max-age:是 HTTP 快取控制最常用的屬性,max-age=30”就是資源的有效時間,相當于告訴瀏覽器,“這個頁面只能快取 30 秒,之后就算是過期,不能用,這也就是經常說的強快取,
- no-store:不允許快取,用于某些變化非常頻繁的資料,例如秒殺頁面;
- no-cache:它的字面含義容易與 no-store 搞混,實際的意思并不是不允許快取,而是可以快取,但在使用之前必須要去服務器驗證是否過期,是否有最新的版本,這也就是經常說的協商快取;
- must-revalidate:又是一個和 no-cache 相似的詞,它的意思是如果快取不過期就可以繼續使用,但過期了如果還想用就必須去服務器驗證,
強快取
強快取兩個相關的欄位是Expires、Cache-Control,HTTP1.0版本使用的是Expires,HTTP1.1使用的是Cache-Control,當這兩個欄位同時在頭部欄位中出現時已Cache-Control 為準,
協商快取
當 Cache-Control 的值是 no-cache、max-age=0、must-revalidate 中的任意一個時,表示客戶端可以快取資源,但是每次使用快取資源前都必須重新驗證是否需要重新獲取資源, 條件請求HTTP 協議定義了一系列“If”開頭的“條件請求”欄位,專門用來檢查驗證資源是否過期,把兩個請求才能完成的作業合并在一個請求里做,而且,驗證的責任也交給服務器,瀏覽器只需等待驗證的結果,
我們最常用的欄位時if-Modified-Since和If-None-Match這兩個,
在第一次的回應報文里提供Last-modified和ETag,然后第二次請求時就可以帶上快取里的原值,驗證資源是否是最新的,如果資源沒有變,服務器就回應一個“304 Not Modified”,表示快取依然有效,瀏覽器就可以更新一下有效期,然后放心大膽地使用快取了,
- if-Modified-Since:上一次回應頭里的Last-modified
- If-None-Match:上一次回應頭里的ETag
ETag
ETag是服務器根據當前檔案的內容,對檔案生成唯一的標識,比如MD5演算法,只要里面的內容有改動,這個值就會修改,服務器通過把回應頭把該欄位發送給瀏覽器,
瀏覽器接受到ETag值,會在下次請求的時候,將這個值作為If-None-Match這個欄位的內容,發給服務器,
服務器接收到If-None-Match后,會跟服務器上該資源的ETag進行比對
- 如果兩者一樣的話,直接回傳304,告訴瀏覽器直接使用快取
- 如果不一樣的話,說明內容更新了,回傳新的資源,跟常規的HTTP請求回應的流程一樣
Etag 一般由web 應用服務器生成,根據修改時間、內容大小等生成一個字串,可以理解為通過某種演算法生成 如MD5; 比如
nginx 中 etag 由回應頭的 Last-Modified 與 Content-Length 表示為十六進制組合而成,不同的web 服務器采用的演算法應該不一樣;
etag 還分強、弱,通常都是采用弱etag,強etag 耗時間,消耗資源,具體的可以查閱相關資料;
下篇在繼續說說https、http2、對頭阻塞、websocket等相關內容參考
透視HTTP協議 ---------Chrono轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/347026.html
標籤:Html/Css
