超文本傳輸協議(英語:HyperText Transfer Protocol,縮寫:HTTP)是一種用於分佈式、協做式和超媒體信息系統的應用層協議。HTTP是萬維網的數據通訊的基礎。html
設計HTTP最初的目的是爲了提供一種發佈和接收HTML頁面的方法。經過HTTP或者HTTPS協議請求的資源由統一資源標識符(Uniform Resource Identifiers,URI)來標識。前端
最開始的網絡分層是分了有七層,以下;java
OSI模型:linux
後來因爲最上面三層(應用層、表示層和會話層)在TCP/IP組中是一個應用層,就合併到應用層了;git
TCP/IP組模型:github
而HTTP在網絡分層中是屬於應用層的協議;web
http的工做方式一般是由客戶端和服務端協同完成的;數據庫
一般,由客戶端發起一個請求,建立一個到服務器指定端口(默認是80)的TCP連接,服務器則在這個端口監聽客戶端的請求,當接收到請求後,會根據客戶端的請求,返回對應的內容,好比html文本,圖片之類的,而且返回對應的狀態碼來告訴客戶端請求的狀態是否成功;json
HTTP報文是在HTTP應用程序之間發送的數據塊。跨域
將http比喻成快遞,那麼http報文就是包裹的快遞單,包含來姓名,地址,郵政編碼,表示我這個快遞要寄到哪裏去,而快遞中心收到這個快遞後,就會經過這個快遞單,來判斷這個包裹要寄送到哪裏去,而這個信息就是經過快遞單來獲取的;
那麼一樣http報文也是相似的道理,客戶端發送一個請求,帶上報文,服務端接收到請求後,解析這個報文,就知道客戶端須要獲取什麼東西來;
而服務端想客戶端返回內容時,也會帶上報文,表示我返回的內容是什麼,方便客戶端經過報文來解析數據,並處理的流程;
http報文分爲請求報文和相應報文;
請求報文的格式:
由請求行,請求頭,空行,請求數據這四部分組成
舉例:請求百度的地址,打開瀏覽器的調試功能,查看請求報文;
響應報文的格式:
由狀態行,請求頭,空行,請求數據這四部分組成
舉例:一樣經過瀏覽器查看百度頁面的響應報文;
URL,全稱:Uniform Resource Locator 譯名:統一資源定位符,用於準確描述Internet上某一資源的地址;
咱們訪問的網頁都是有地址的,而地址一般指向某個服務器上的資源;
URL的格式是由協議類型(http,https等),服務器地址(host),端口號(port),路徑(path)這幾部分組成的;
好比百度的地址:www.baidu.com/;
格式以下:http://host[:port][/path]
HTTP請求的方法有不少,以下:
GET:使用GET的請求用於從服務器獲取數據;
HEAD:和GET請求類似,可是沒有響應體;
POST方法:用於將內容提交到服務器,一般用於修改或者刪除服務器上的資源;
PUT方法:一般用於修改服務器上的數據;
DELETE方法:刪除服務器指定的資源;
CONNECT方法:創建一個到由目標資源標識的服務器的隧道,用於代理服務器;
OPTIONS方法:用於描述目標資源的通訊選項,一般用於跨域請求;
TRACE方法:沿着到目標資源的路徑執行一個消息環回測試,用於追蹤請求;
咱們最經常使用的的請求方法,基本上就是GET和POST請求了,那麼咱們來看一下他們的區別吧;
一般在瀏覽器上輸入一個地址進行請求,通常都是經過GET請求方式,而POST請求通常用於提交內容,將須要提交給服務器的參數放到body裏面,進行請求;
來看看w3school列舉的差別點:
疑問:
1,安全性:
GET的請求在瀏覽器上是能夠看到請求的參數的,基本上來講沒有安全性可言;
而POST請求在瀏覽器上看不到請求參數,是否是表示就是安全的呢?
並非,別人能夠經過抓包的方式來獲取到你的請求參數,因此並非安全的,要想安全的傳輸只能經過有加密方式的HTTPS請求;
2,POST方法是否會產生兩個TCP數據包?
答案是:不必定;
這個不是必然會產生兩個TCP數據包,而是看瀏覽器是否作了發送兩次TCP數據包的處理,若是有作處理的話就會發送兩次TCP數據包;
網上已經有文章驗證過了,我這裏就再也不多說了,經驗證的結果爲:Chrome和Safari瀏覽器會發送兩次TCP數據包,而Firefox瀏覽器只發送了一次;
詳情請參考:據說『99% 的人都理解錯了 HTTP 中 GET 與 POST 的區別』??
3,URL過長會致使什麼問題?
咱們先用postman來模擬一下看看,弄一個超長的URL,請求一下看會不會報錯;
從圖片上能夠看出,postman直接返回了414 Request-URL Too Long,表示當前URL過長了;
用瀏覽器請求返回的錯誤信息也是同樣:
那麼這裏是否會有疑問,爲何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 來發送數據呢?
狀態碼是客戶端向服務端請求後,服務端會返回一個數字來告訴端側請求的狀態,好比200,表示請求成功了,好比404,客戶端錯誤了;這樣客戶端再拿到狀態碼以後再作相應的處理,好比是否重試,好比展現錯誤頁面,好比提示用戶操做不當之類的;
看一下菜鳥教程對於狀態碼的定義:
如圖所示,http狀態碼主要分爲5種類型,分別爲1xx,2xx,3xx,4xx,5xx等開頭的狀態碼;
每個狀態碼以x開頭的均可歸類爲某一種類型的狀態;好比200(請求成功),404(客戶端錯誤)等;
狀態碼的種類有不少,這裏就不一一介紹了;
Header實際上是一個鍵值對,是屬於元數據,用於告訴服務器,我要取什麼樣的數據或者我要作什麼樣的操做;
好比:Accept: text/plain,表示客戶端能接受文本數據的返回;
除了先有的一些標準的請求頭,咱們還能夠自定義請求頭,好比:user:"張三",表示我要傳 "張三"的Header到服務器,服務器再作相應的處理;
HTTP頭字段按照實際用途能夠分爲四種類型,分別爲通用頭,請求頭,響應頭和實體頭這四種;
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請求頭與響應頭
1,經過網絡獲取內容,會受速度影響而且開銷很大,須要在客戶端和服務器之間創建通信,而後經過傳包的方式來進行通信,受網絡速度影響,有可能會響應比較慢,致使前端的展現體驗很差;
2,減小開銷,若是每一次請求都從服務器取的話,那麼服務器將會面臨巨大的壓力,有可能會掛掉,致使訪問不了;
3,使用緩存還能夠減小網絡帶寬的佔用,過多的請求會致使網絡阻塞,從而響應速度也變慢;
4,減小無心義的重複請求,好比某個頁面的數據,運營一天才會去修改一次,可是我每次進來這個頁面都去從服務器請求數據,這樣是沒有意義的,只會致使資源的浪費;
因此使用緩存能夠大大的提高響應速度,下降服務器壓力,減小帶寬的佔用,所以HTTP的緩存是相當重要的;
1,經過 ETag 驗證緩存的響應
(1)ETag是什麼?
ETag 本質上是一個header,也被稱爲驗證令牌,是由服務器根據根據文件生成的hash值或者其餘的某個值;
(2)驗證令牌的誕生旨在解決什麼問題?
假如咱們本地的數據是有緩存時效的,當咱們從本地取數據的時候,發現緩存時效過時了,這時候就會去服務器那邊取數據,可是此時從服務器取回來的數據和本地的數據是同樣的,沒有什麼變化,只是本地的緩存時效過時了,這時候從服務器取回來的數據就沒有意義了,還浪費了請求所消耗的資源;
那麼驗證令牌就是爲了解決這類問題而誕生的;
(3)驗證令牌是怎麼解決這類問題的?
前面說了,驗證令牌本質上是一個header,咱們去服務器取數據的時候會帶上這個header,好比 ETag:"xxxxxx"; 服務器收到這個header以後,就會去作驗證,若是驗證對比了令牌後發現,沒有變化,則返回「304 Not Modified」響應,告訴瀏覽器緩存重點數據沒有發生什麼變化,能夠繼續使用,那麼咱們接受到響應後,更新本地的緩存時效,進而繼續使用緩存;
借用官方的圖:
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等)緩存數據,可是瀏覽器能夠緩存該指令的數據;
三、Expires緩存頭
Expires相應頭包含日期和時間,下次請求時,會將本地時間和這個時間作比較,若是若是緩存有效,則取緩存,可是因爲本地時間和服務器時間不一樣步,用這個來判斷緩存時效會存在不許的問題,所以在HTTP1.1以後更多的是使用Cache-Control 指令,更加靈活;
四、Cache-Control在okhttp中的體現
final CacheControl.Builder builder = new CacheControl.Builder();
builder.noCache();//不使用緩存,所有走網絡
builder.noStore();//不使用緩存,也不存儲緩存
builder.maxAge(10, TimeUnit.MILLISECONDS);//指示客戶機能夠接收生存期不大於指定時間的響應。
CacheControl cache = builder.build();//cacheControl
複製代碼
萬維網協會(World Wide Web Consortium,W3C)和互聯網工程任務組(IETF)在1991年的時候制定了 HTTP 0.9 標準,那時候只支持GET方法請求;
從單一的GET方法請求,新增了POST請求,支持發送任何格式的內容,包括文本,視頻,音樂,和二進制文件;
請求體和返回體的格式也變了,新增了頭信息(Header),狀態碼(status code),多字符集支持、多部分發送(multi-part type)、權限(authorization)、緩存(cache)、內容編碼(content encoding)等功能;
缺點:
(1)鏈接沒法複用,瀏覽器和服務器只能創建短暫的TCP鏈接,當請求完畢以後,就關閉該TCP鏈接;若是須要從新請求,則須要從新創建TCP鏈接,會致使資源的浪費,以及響應的時間;
(2)Head-Of-Line Blocking(HOLB,隊頭阻塞),在TCP鏈接中,請求是有序的,只有當服務器處理完前一個請求後,纔會處理下一個請求,若是前一個請求速度較慢,就會形成後續請求阻塞等待的狀況出現;
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),即瀏覽器能夠同時發送多個請求給服務器,沒必要等到上一個請求返回以後再進行請求,可是服務器的處理是等處理完當前請求的響應以後,纔會去處理下一個請求的響應,即便當前不少請求都已經處理完了,服務器仍是得根據請求的順序來進行響應;
所以它不是真正的多請求協議,可是是一個很好的改進;
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兆,文件總共有20兆,此時的斷點下載就是從10兆開始進行續傳,繼續下載的;
經過http的Range頭字段來實現的,Range頭字段支持從服務器或者指定內容範圍大小的文件,好比我想要或者某個文件的前1024字節的數據,那麼我只須要傳 Range: bytes=0-1024 (0到1024字節的數據),這樣就能夠從服務器獲取到對於字節的數據了;
斷點續傳的好處就是傳輸效率高,好比下載一個文件,在傳輸過程當中受網絡影響中斷了,這時不須要在開頭從新下載,而是在中止的地方繼續下載;
斷點續傳還能夠經過多線程來提升效率,好比下載一個文件,我能夠開10個線程或者20個線程來分段下載文件,這樣能夠大大的提升下載速度,像迅雷或者百度雲大多都是這種原理,只是更加複雜;至於最多能開多少線程進行下載, 要看當前客戶端的性能;
數據庫:用於存儲斷點下載的其實位置;
多線程:用於加快下載速度;
RandomAccessFile類:Java提供的對文件內容的訪問,既能夠讀文件也能夠寫文件,能夠訪問文件的任意位置適用於由大小已知的記錄組成的文件;
下面從一張圖來看看具體實現: