
最近看了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 等新方法
但是此時的hppt1.0 并不是一個“标準”,隻是記錄已有實踐和模式的一份參考文檔,不具有實際的限制力
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 相似的詞,它的意思是如果緩存不過期就可以繼續使用,但過期了如果還想用就必須去伺服器驗證。
不止伺服器可以設定“Cache-Control”頭,浏覽器也可以設定“Cache-Control”,也就是說請求 - 應答的雙方都可以用這個字段進行緩存控制,互相協商緩存的使用政策。
強緩存
強緩存兩個相關的字段是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
如果資源沒有變,伺服器就回應一個“304 Not Modified”,表示緩存依然有效,浏覽器就可以更新一下有效期,然後放心大膽地使用緩存了。
ETag
ETag是伺服器根據目前檔案的内容,對檔案生成唯一的辨別,比如MD5算法,隻要裡面的内容有改動,這個值就會修改,伺服器通過把響應頭把該字段發送給浏覽器。
浏覽器接受到ETag值,會在下次請求的時候,将這個值作為If-None-Match這個字段的内容,發給伺服器。
伺服器接收到If-None-Match後,會跟伺服器上該資源的ETag進行比對
- 如果兩者一樣的話,直接傳回304,告訴浏覽器直接使用緩存
- 如果不一樣的話,說明内容更新了,傳回新的資源,跟正常的HTTP請求響應的流程一樣
下篇在繼續說說https、http2、對頭阻塞、websocket等相關内容
參考
透視HTTP協定 ---------Chrono