Android 你不得不學的HTTP相關知識

Amter發表於2020-04-07

本章目錄

  • 1、http是怎麼定義的?
  • 2、http是怎麼工作的?
  • 3、報文是什麼?
  • 4、URL是什麼?
  • 5、RequestMethod請求方法有哪些?
  • 6、State Code狀態碼
  • 7、Header頭部
  • 8、Cache快取
  • 9、http的發展史
  • 10、斷點續傳功能是怎麼實現的

本章結構圖

image.png

1、http是怎麼定義的?

1.1、超文字傳輸協議;

超文字傳輸協議(英語:HyperText Transfer Protocol,縮寫:HTTP)是一種用於分散式、協作式和超媒體資訊系統的應用層協議。HTTP是全球資訊網的資料通訊的基礎。

設計HTTP最初的目的是為了提供一種釋出和接收HTML頁面的方法。通過HTTP或者HTTPS協議請求的資源由統一資源識別符號(Uniform Resource Identifiers,URI)來標識。

1.2、http在網路中的分層;

最開始的網路分層是分了有七層,如下;

OSI模型:

Android 你不得不學的HTTP相關知識

後來由於最上面三層(應用層、表示層和會話層)在TCP/IP組中是一個應用層,就合併到應用層了;

TCP/IP組模型:

Android 你不得不學的HTTP相關知識

而HTTP在網路分層中是屬於應用層的協議;

2、http是怎麼工作的?

http的工作方式通常是由客戶端和服務端協同完成的;

通常,由客戶端發起一個請求,建立一個到伺服器指定埠(預設是80)的TCP連結,伺服器則在這個埠監聽客戶端的請求,當接收到請求後,會根據客戶端的請求,返回對應的內容,比如html文字,圖片之類的,並且返回對應的狀態碼來告訴客戶端請求的狀態是否成功;

3、報文是什麼?

3.1、http的報文是什麼?

HTTP報文是在HTTP應用程式之間傳送的資料塊。

將http比喻成快遞,那麼http報文就是包裹的快遞單,包含來姓名,地址,郵政編碼,表示我這個快遞要寄到哪裡去,而快遞中心收到這個快遞後,就會通過這個快遞單,來判斷這個包裹要寄送到哪裡去,而這個資訊就是通過快遞單來獲取的;

那麼同樣http報文也是類似的道理,客戶端傳送一個請求,帶上報文,服務端接收到請求後,解析這個報文,就知道客戶端需要獲取什麼東西來;

而服務端想客戶端返回內容時,也會帶上報文,表示我返回的內容是什麼,方便客戶端通過報文來解析資料,並處理的流程;

3.2、http報文的格式

http報文分為請求報文和相應報文;

  • 請求報文:客戶端向伺服器傳送的報文;
  • 相應報文:服務端向客戶端傳送的報文;

請求報文的格式:

由請求行,請求頭,空行,請求資料這四部分組成

Android 你不得不學的HTTP相關知識

舉例:請求百度的地址,開啟瀏覽器的除錯功能,檢視請求報文;

Android 你不得不學的HTTP相關知識

響應報文的格式:

由狀態行,請求頭,空行,請求資料這四部分組成

Android 你不得不學的HTTP相關知識

舉例:同樣通過瀏覽器檢視百度頁面的響應報文;

Android 你不得不學的HTTP相關知識

4、URL是什麼?

4.1、URL是怎麼定義的?

URL,全稱:Uniform Resource Locator 譯名:統一資源定位符,用於準確描述Internet上某一資源的地址;

我們訪問的網頁都是有地址的,而地址通常指向某個伺服器上的資源;

4.2、URL的格式是怎樣的?

URL的格式是由協議型別(http,https等),伺服器地址(host),埠號(port),路徑(path)這幾部分組成的;

比如百度的地址:www.baidu.com/;

格式如下:http://host[:port][/path]

  • 協議型別:表示使用哪種網路協議來進行網路請求,比如HTTP,HTTPS;
  • 伺服器地址:表示主機,或者域名,或者IP地址;
  • 埠號:如果沒有指定的話,預設為80;
  • 路徑:表示指定請求資源的URL,預設會帶上“/”,一般都是瀏覽器給我們加上的;

5、RequestMethod請求方法有哪些?

5.1、請求方法

HTTP請求的方法有很多,如下:

  • GET:使用GET的請求用於從伺服器獲取資料;

  • HEAD:和GET請求相似,但是沒有響應體;

  • POST方法:用於將內容提交到伺服器,通常用於修改或者刪除伺服器上的資源;

  • PUT方法:通常用於修改伺服器上的資料;

  • DELETE方法:刪除伺服器指定的資源;

  • CONNECT方法:建立一個到由目標資源標識的伺服器的隧道,用於代理伺服器;

  • OPTIONS方法:用於描述目標資源的通訊選項,通常用於跨域請求;

  • TRACE方法:沿著到目標資源的路徑執行一個訊息環回測試,用於追蹤請求;

5.2、GET和POST請求的區別

我們最常用的的請求方法,基本上就是GET和POST請求了,那麼我們來看一下他們的區別吧;

通常在瀏覽器上輸入一個地址進行請求,一般都是通過GET請求方式,而POST請求一般用於提交內容,將需要提交給伺服器的引數放到body裡面,進行請求;

來看看w3school列舉的差異點:

Android 你不得不學的HTTP相關知識

疑問:
1,安全性:

GET的請求在瀏覽器上是可以看到請求的引數的,基本上來說沒有安全性可言;

而POST請求在瀏覽器上看不到請求引數,是不是表示就是安全的呢?

並不是,別人可以通過抓包的方式來獲取到你的請求引數,所以並不是安全的,要想安全的傳輸只能通過有加密方式的HTTPS請求;

2,POST方法是否會產生兩個TCP資料包?

答案是:不一定;

這個不是必然會產生兩個TCP資料包,而是看瀏覽器是否做了傳送兩次TCP資料包的處理,如果有做處理的話就會傳送兩次TCP資料包;

網上已經有文章驗證過了,我這裡就不再多說了,經驗證的結果為:Chrome和Safari瀏覽器會傳送兩次TCP資料包,而Firefox瀏覽器只傳送了一次;

詳情請參考:聽說『99% 的人都理解錯了 HTTP 中 GET 與 POST 的區別』??

3,URL過長會導致什麼問題?

我們先用postman來模擬一下看看,弄一個超長的URL,請求一下看會不會報錯;

Android 你不得不學的HTTP相關知識

從圖片上可以看出,postman直接返回了414 Request-URL Too Long,表示當前URL過長了;

用瀏覽器請求返回的錯誤資訊也是一樣:

Android 你不得不學的HTTP相關知識

那麼這裡是否會有疑問,為什麼URL過長會報錯? 是伺服器處理不了,還是postman處理不了? 還是http協議規定的URL不能超過多長呢?

檢視RFC的http的相關文件(RFC 2616 - HTTP/1.1)發現裡面的一段話,如下:

The HTTP protocol does not place any a priori limit on the length of a URI. Servers MUST be able to handle the URI of any resource they serve, and SHOULD be able to handle URIs of unbounded length if they provide GET-based forms that could generate such URIs. A server SHOULD return 414 (Request-URI Too Long) status if a URI is longer than the server can handle (see section 10.4.15).

翻譯過來的意思就是:

HTTP協議沒有對長度限制進行任何限制,伺服器必須能夠處理其任何資源的URI服務,並且應該能夠處理長度不受限制的URI,如果URI較長,則應返回414(請求URI太長)狀態超出伺服器的處理能力;

看到這裡,我們就明白了,http協議並沒有限制URL的長度,對URL長度做處理的,只有伺服器或者瀏覽器,所以URL過長導致的報錯是由伺服器或者瀏覽器處理的;

4,GET方法可以帶Body嗎?

答案是:可以的!

為什麼GET方法可以帶Body嗎?不是說GET方法是通過URL來取資源的嗎?

來看一下RFC的協議中,對於GET方法的定義:

The GET method means retrieve whatever information (in the form of an entity) is identified by the Request-URI.

翻譯過來的意思就是GET方法是通過URL來檢索伺服器上的資訊,並沒有說不能帶上Body;

那麼到這裡你是否就有疑惑了? 他沒說不就代表他可以帶上Body來請求啊;別急,我們繼續分析;

再來看另外一份RFC協議對於GET請求帶Body的解釋:

A payload within a GET request message has no defined semantics; sending a payload body on a GET request might cause some existing implementations to reject the request.

翻譯過來的意思就是帶上Body的GET方法,可能會導致拒絕請求,有可能是伺服器拒絕了,也有可能是瀏覽器或者框架拒絕請求;

結論:GET方法是可以帶上Body請求的,但是可能會發生拒絕請求的錯誤,或者一些其他的錯誤出現;

那麼對於GET方法帶上Body的測試這裡就不多說了,感興趣的請參考:誰說 HTTP GET 就不能通過 Body 來傳送資料呢?

6、State Code狀態碼

6.1、狀態碼是什麼?

狀態碼是客戶端向服務端請求後,服務端會返回一個數字來告訴端側請求的狀態,比如200,表示請求成功了,比如404,客戶端錯誤了;這樣客戶端再拿到狀態碼之後再做相應的處理,比如是否重試,比如展示錯誤頁面,比如提示使用者操作不當之類的;

6.1、狀態碼有哪些?

看一下菜鳥教程對於狀態碼的定義:

Android 你不得不學的HTTP相關知識

如圖所示,http狀態碼主要分為5種型別,分別為1xx,2xx,3xx,4xx,5xx等開頭的狀態碼;

每一個狀態碼以x開頭的都可歸類為某一種型別的狀態;比如200(請求成功),404(客戶端錯誤)等;

狀態碼的種類有很多,這裡就不一一介紹了;

7、Header頭部

7.1、Header是用來幹嘛的?

Header其實是一個鍵值對,是屬於後設資料,用於告訴伺服器,我要取什麼樣的資料或者我要做什麼樣的操作;

比如:Accept: text/plain,表示客戶端能接受文字資料的返回;

除了先有的一些標準的請求頭,我們還可以自定義請求頭,比如:user:"張三",表示我要傳 "張三"的Header到伺服器,伺服器再做相應的處理;

7.2、Header有哪些?

HTTP頭欄位按照實際用途可以分為四種型別,分別為通用頭,請求頭,響應頭和實體頭這四種;

  • 通用頭:是客戶端和伺服器都可以使用的頭部,可以在客戶端、伺服器和其他應用程式之間提供一些非常有用的通用功能,如Date頭部;
  • 請求頭:是請求報文特有的,它們為伺服器提供了一些額外資訊,比如客戶端希望接收什麼型別的資料,如Accept頭部;
  • 響應頭:便於客戶端提供資訊,比如,客服端在與哪種型別的伺服器進行互動,如Server頭部;
  • 實體頭:指的是用於應對實體主體部分的頭部,比如,可以用實體頭部來說明實體主體部分的資料型別,如Content-Type頭部;

7.3、常見的Header的型別有哪些?

1,Host

客戶端指定自己想訪問的WEB伺服器的域名/IP 地址和埠號;

2,Content-Type

用於指定請求體的型別,主要有四種;

(1)text/html:用於告訴伺服器需要響應的型別為文字資料型別;

(2)x-www-form-urlencoded:表單型別,用於web頁面純文字表單提交資料到伺服器,比如註冊頁面資料的提交;

(3)multitype/form-data:用於web頁面帶二進位制檔案的表單提交方式,比如修改使用者頭像,會上傳圖片到伺服器;

(4)application/json , image/jpeg , application/zip ...:提交單項內容到伺服器,比如提交json,提交image,提交zip包到伺服器;

3,Content-Length

用於指定響應體的長度,表示我這次請求需要返回多少位元組的內容,多用於分塊傳輸;

4,User-Agent

用於向服務端表明身份資訊,表示我是來自手機客戶端的請求,還是來自某個瀏覽器的請求;

5,Range

表示想從伺服器取哪部分的內容,比如byte:start-end;用於多執行緒下載或者斷點續傳;

6,Accept

告訴伺服器客戶端能接受什麼型別的資料,比如text/html;

7,Accept-Charset

告訴伺服器客戶端能接受的的字符集,比如utf-8;

8,Accept-Encoding

告訴伺服器客戶端能接受的壓縮編碼型別,如zip;

9,Content-Encoding

伺服器告訴客戶端自己使用了什麼壓縮方法,如gzip,deflate等;

HTTP的Header型別是在是太多了,這裡就不一一介紹了,感興趣的請參考: 「Android系列之網路(二)----HTTP請求頭與響應頭

8、Cache快取

8.1、http為什麼需要快取?

1,通過網路獲取內容,會受速度影響並且開銷很大,需要在客戶端和伺服器之間建立通訊,然後通過傳包的方式來進行通訊,受網路速度影響,有可能會響應比較慢,導致前端的展示體驗不好;

2,減少開銷,如果每一次請求都從伺服器取的話,那麼伺服器將會面臨巨大的壓力,有可能會掛掉,導致訪問不了;

3,使用快取還可以減少網路頻寬的佔用,過多的請求會導致網路阻塞,從而相應速度也變慢;

4,減少無意義的重複請求,比如某個頁面的資料,運營一天才會去修改一次,但是我每次進來這個頁面都去從伺服器請求資料,這樣是沒有意義的,只會導致資源的浪費;

所以使用快取可以大大的提升響應速度,降低伺服器壓力,減少頻寬的佔用,因此HTTP的快取是至關重要的;

8.2、http的快取機制是什麼?

1,通過 ETag 驗證快取的響應

(1)ETag是什麼?

ETag 本質上是一個header,也被稱為驗證令牌,是由伺服器根據根據檔案生成的hash值獲取其他的某個值;

(2)驗證令牌的誕生旨在解決什麼問題?

假如我們本地的資料是有快取時效的,當我們從本地取資料的時候,發現快取時效過期了,這時候就會去伺服器那邊取資料,但是此時從伺服器取回來的資料和本地的資料是一樣的,沒有什麼變化,只是本地的快取時效過期了,這時候從伺服器取回來的資料就沒有 意義了,還浪費了請求所消耗的資源;

那麼驗證令牌就是為了解決這類問題而誕生的;

(3)驗證令牌是怎麼解決這類問題的?

前面說了,驗證本質上是一個header,我們去伺服器取資料的時候會帶上這個header,比如 ETag:"xxxxxx"; 伺服器收到這個header之後,就會去做驗證,如果驗證對比了令牌後發現,沒有變化,則返回“304 Not Modified”響應,告訴瀏覽器快取重點資料沒有發生什麼變化,可以繼續使用,那麼我們接受到響應後,更新本地的快取時效,進而繼續使用快取;

借用官方的圖:

image.png

2,Cache-Control 快取控制

(1)Cache-Control是用來幹嘛的?

Cache-Control是用來定義快取策略的,比如定義某個資源在什麼場景下,快取多長的時間,本質上是一個header;

Cache-Control是在 HTTP/1.1 規範中定義的;

(2)Cache-Control怎麼定義快取策略?

Cache-Control通過header定義的快取指令來實現快取策略,比如Cache-Control:max-age = 180;

(3)快取指令有哪些?

請求指令

  • no-cache:會先通過ETag驗證令牌和伺服器進行通訊,判斷伺服器的資料是否有修改過,如果有修改過,則使用伺服器返回的新的資料,否則的話就取快取,使用這個指令會和伺服器進行一次通訊;

  • no-store:指令為"no-store"的情況下,一律不進行快取,都是從伺服器取資料,一般用於需要安全的場景或者需要實時重新整理的場景;

  • max-age:表示當前請求的響應體的有效時間為多長,超過了這個時效則從伺服器取資料;

  • public:表示可以被任何中間者(代理伺服器,cdn等)或者瀏覽器快取資料,通常情況下,public並不是必須的指令,有其他指令(比如max-age)表示了該請求可以被快取;

  • private:表示不可以被任何中間者(代理伺服器,cdn等)快取資料,但是瀏覽器可以快取該指令的資料;

3、Expires快取頭

Expires相應頭包含日期和時間,下次請求時,會將本地時間和這個時間做比較,如果如果快取有效,則取快取,但是由於本地時間和伺服器時間不同步,用這個來判斷快取時效會存在不準的問題,因此在HTTP1.1之後更多的是使用Cache-Control 指令,更加靈活;

4、Cache-Control在okhttp中的體現

  final CacheControl.Builder builder = new CacheControl.Builder();
            builder.noCache();//不使用快取,全部走網路
            builder.noStore();//不使用快取,也不儲存快取
            builder.maxAge(10, TimeUnit.MILLISECONDS);//指示客戶機可以接收生存期不大於指定時間的響應。
            CacheControl cache = builder.build();//cacheControl
複製程式碼

9、http的發展史

9.1、HTTP 0.9

全球資訊網協會(World Wide Web Consortium,W3C)和網際網路工程任務組(IETF)在1991年的時候制定了 HTTP 0.9 標準,那時候只支援GET方法請求;

9.2、HTTP 1.0

從單一的GET方法請求,新增了POST請求,支援傳送任何格式的內容,包括文字,視訊,音樂,和二進位制檔案;

請求體和返回體的格式也變了,新增了頭資訊(Header),狀態碼(status code),多字符集支援、多部分傳送(multi-part type)、許可權(authorization)、快取(cache)、內容編碼(content encoding)等功能;

缺點

(1)連線無法複用,瀏覽器和伺服器只能建立短暫的TCP連線,當請求完畢之後,就關閉該TCP連線;如果需要重新請求,則需要重新建立TCP連線,會導致資源的浪費,以及響應的時間;

(2)Head-Of-Line Blocking(HOLB,隊頭阻塞),在TCO連線中,請求是有序的,只有當伺服器處理完前一個請求後,才會處理下一個請求,如果前一個請求速度較慢,就會造成後續請求阻塞等待的情況出現;

9.3、HTTP 1.1

HTTP 1.1是當前最流行的http版本;

優化點: (1)快取:在 HTTP 1.0 中主要使用 header 裡的 If-Modified-Since,Expires 來做為快取判斷的標準,HTTP 1.1則引入了更多的快取控制策略例如 Entity tag,If-Unmodified-Since等更多可供選擇的快取頭來控制快取策略。

(2)頻寬的優化:新增了range頭欄位,用於分塊傳輸,允許請求當前檔案的部分內容,這樣可以大大減少網路頻寬的佔用;

(3)新增了24 個錯誤狀態響應碼

(4)長連結:頭欄位新增了Connection:keep-alive,表示可以複用一部分連結,在tcp連線上可以傳達多個請求,以此來減少建立和連線帶來的消耗;

(5)增加管線化技術(pipelining),在當前請求還沒有返回時,可以傳送下一個請求到伺服器,以此來降低請求時間;

缺點

(1)長連結:雖然加入長連結可以減少建立和連線帶來的消耗,但是不同的域名的連結不能複用,只能重新建立長連結,會耗費資源,並且給伺服器帶來巨大的壓力;

(2)在header中攜帶請求的資料量過大,造成流量的浪費,如果每次請求header的內容不變,但是header攜帶的資料量又很大的情況下,就會造成資源的浪費;

(3)HTTP 1.1雖然引入了pipelining來解決隊頭阻塞問題(Head-Of-Line Blocking),即瀏覽器可以同時傳送多個請求給伺服器,不必等到上一個請求返回之後再進行請求,但是伺服器的處理是等處理完當前請求的響應之後,才會去處理下一個請求的響應,即使當前很多請求都已經處理完了,伺服器還是得根據請求的順序來進行響應;

因此它不是真正的多請求協議,但是是一個很好的改進;

9.4、HTTP 2.0

HTTP2.0基於谷歌開發的SPDY協議,而HTTP 2.0相對於HTTP 1.1帶來了什麼新的改進呢?

優化點:

(1)多路複用:對於HTTP1.1中,如果當前有多個請求,請求的傳送都是序列執行的,對於寬頻的利用效率不高,但是在HTTP 2.0中,多個請求可以並行請求,大大的提升了寬頻的利用率;

(2)Header壓縮:使用首部表來跟蹤和儲存之前傳送的鍵值對,對於相同的內容,不會再每次請求和響應時傳送。

(3)資料優先順序:由於請求可以併發傳送給伺服器,但是伺服器還是遵循先進先出的規則來處理請求,但是在HTTP 2.0中可以設定當前請求的優先順序,這樣伺服器在處理請求的響應時,會優先返回優先順序較高的請求;

(4)服務端推送:對於請求,一般都是瀏覽器或者客戶端傳送給伺服器,伺服器處理之後再返回,但是在HTTP 2.0中伺服器可以推送相關檔案給客戶端,客戶端再進行相應的處理,而不必等客戶端傳送請求之後再返回給客戶端;

10、斷點續傳功能是怎麼實現的?

10.1、原理

原理很簡單,就是從停止下載的地方繼續下載,比如我下載了10兆,檔案總共有20兆,此時的斷點下載就是從10兆開始進行續傳,繼續下載的;

10.2、核心

通過http的Range頭欄位來實現的,Range頭欄位支援從伺服器或者指定內容範圍大小的檔案,比如我想要或者某個檔案的前1024位元組的資料,那麼我只需要傳 Range: bytes=0-1024 (0到1024位元組的資料),這樣就可以從伺服器獲取到對於位元組的資料了;

10.3、好處

斷點續傳的好處就是傳輸效率高,比如下載一個檔案,在傳輸過程中受網路影響中斷了,這時不需要在開頭重新下載,而是在停止的地方繼續下載;

斷點續傳還可以通過多執行緒來提高效率,比如下載一個檔案,我可以開10個執行緒或者20個執行緒來分段下載檔案,這樣可以大大的提高下載速度,像迅雷或者百度雲大多都是這種原理,只是更加複雜;至於最多能開多少執行緒進行下載, 要看當前客戶端的效能;

10.3、客戶端的實現

資料庫:用於儲存斷點下載的其實位置;

多執行緒:用於加快下載速度;

RandomAccessFile類:Java提供的對檔案內容的訪問,既可以讀檔案也可以寫檔案,可以訪問檔案的任意位置適用於由大小已知的記錄組成的檔案;

下面從一張圖來看看具體實現:

image.png

參考&感謝

關於我

兄dei,如果我的文章對你有幫助的話,請幫我點個贊吧️,也可以關注一下我的Github部落格;

相關文章