Android開發之漫漫長途 XIX——HTTP

來源:互聯網
上載者:User

該文章是一個系列文章,是本人在Android開發的漫漫長途上的一點感想和記錄,我會盡量按照先易後難的順序進行編寫該系列。該系列引用了《Android開發藝術探索》以及《深入理解Android 卷Ⅰ,Ⅱ,Ⅲ》中的相關知識,另外也借鑒了其他的優質部落格,在此向各位大神表示感謝,膜拜!!!

前言

在開始Android並發系列文章之前先插入一些文章,後續Android並發系列文章會按照計劃發布。本篇文章是來說說HTTP那些事。

HTTP簡介

Web 使用一種名為 HTTP ( HyperText Transfer Protocol ,超文字傳輸通訊協定 (HTTP)的協議作為規範,完成從用戶端到伺服器端等一系列運作流程。本文探討的是HTTP/1.1版本。這仍然是大多數網站採用的HTTP協議
我們先看一下下面這張廣為流傳的圖。

那些與HTTP協議密切相關的協議IP

IP 協議的作用是把各種資料包傳送給對方。而要保證確實傳送到對方那裡,則需要滿足各類條件。其中兩個重要的條件是 IP 位址和 MAC地址( Media Access Control Address )。IP 位址指明了節點被分配到的地址, MAC 位址是指網卡所屬的固定地址。 IP 位址可以和 MAC 位址進行配對。 IP 位址可變換,但 MAC地址基本上不會更改。IP使用 ARP 協議憑藉 MAC 位址進行通訊

TCP(確保可靠性的協議)

按層次分, TCP 位於傳輸層,提供可靠的位元組流服務。所謂的位元組流服務( Byte Stream Service )是指,為了方便傳輸,將大塊資料分割成以報文段( segment )為單位的資料包進行管理。而可靠的傳輸服務是指,能夠把資料準確可靠地傳給對方。

TCP串連的3次握手

為了準確無誤地將資料送達目標處, TCP 協議採用了三向交握( three-way handshaking )策略。
握手過程中使用了 TCP 的標誌( flag ) —— SYN ( synchronize ) 和ACK ( acknowledgement )。發送端首先發送個帶 SYN 標誌的資料包給對方。接收端收到後,回傳一個帶有 SYN/ACK 標誌的資料包以示傳達確認資訊。最後,發送端再回傳一個帶 ACK 標誌的資料包,代表 “ 握手 ” 結束。若在握手過程中某個階段莫名中斷, TCP 協議會再次以相同的順序發送相同的資料包。

整個過程如所示

為什麼需要3次握手

為什麼需要3次握手,如果面試中問到了TCP相關知識,那麼這個問題也幾乎是必問的,為什麼是3次,而不是1次,2次或者4次,5次??

首先我們知道不管是用戶端或者服務端發送資料都會消耗網路流量,還會造成許多不必要的通訊開銷,浪費資源,所以我們在保證TCP可靠性的前提下所經曆的通訊次數自然越少越好。

TCP的可靠性含義我們上面已經說了,那我們就從3次握手分析,如果只有1次握手,用戶端只向服務端發送資料,那麼就談不上可靠性了,因為服務端都沒有回複,那麼我們來看只有2次握手行不行,如果只有2次握手,用戶端只向服務端發送資料,伺服器也向用戶端回複我收到資料了,但是考慮此時如果發送資料的過程中資料丟失了,服務端認為串連建立了(資料我已經發出去了),可是用戶端沒有收到資料,用戶端認為串連沒有建立,就會重複請求,而這對與服務端來說就又是一個全新的串連。一言以蔽之:第三向交握是為了防止:如果用戶端遲遲沒有收到伺服器返回確認報文,這時會放棄串連,重新啟動一條串連請求,但問題是:伺服器不知道用戶端沒有收到,所以他會收到兩個串連,浪費串連開銷。如果每次都是這樣,就會浪費多個串連開銷。

HTTP詳解HTTP的報文結構

用於 HTTP 協議互動的資訊被稱為 HTTP 報文。請求端(用戶端)的HTTP 報文叫做請求報文,響應端(伺服器端)的叫做響應報文。HTTP 報文本身是由多行(用 CR+LF 作分行符號)資料構成的字串文本。HTTP 報文大致可分為報文首部和報文主體兩塊。兩者由最初出現的空行( CR+LF )來劃分。通常,並不一定要有報文主體。

HTTP的請求以及響應報文結構

HTTP的報文頭部

下面的是請求某網站時,請求報文以及響應報文的首部資訊。

請求報文首部

Host: hackr.jpUser-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:61.0) Gecko/20100101 Firefox/61.0Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2Accept-Encoding: gzip, deflateConnection: keep-aliveUpgrade-Insecure-Requests: 1

響應報文首部

HTTP/1.1 200 OKDate: Thu, 23 Aug 2018 08:17:43 GMTServer: ApacheLast-Modified: Tue, 08 Jan 2013 08:53:29 GMTETag: "25e-4d2c3145df440-gzip"Accept-Ranges: bytesVary: Accept-Encoding,User-AgentContent-Encoding: gzipContent-Length: 379Keep-Alive: timeout=15, max=100Connection: Keep-AliveContent-Type: text/html

由上可知HTTP 首部欄位是由首部欄位名和欄位值構成的,中間用冒號 “:” 分隔。

HTTP的報文頭部類型HTTP 首部欄位根據實際用途被分為以下 4 種類型。

通用首部欄位( General Header Fields )請求報文和響應報文兩方都會使用的首部。
| 首部欄位名 | 說明
| ------------- |:-------------:
| Cache-Control|控制緩衝的行為
| Connection|逐跳首部、串連的管理
| Date|建立報文的日期時間
| Pragma|報文指令
| Trailer|報文末端的首部一覽
| Transfer-Encoding|指定報文主體的傳輸編碼方式
| Upgrade|升級為其他協議
| Via|Proxy 伺服器的相關資訊
| Warning|錯誤通知

請求首部欄位( Request Header Fields )從用戶端向伺服器端發送請求報文時使用的首部。補充了請求的附加內容、用戶端資訊、響應內容相關優先順序等資訊。
| 首部欄位名 | 說明
| ------------- |:-------------:
| Accept| 使用者代理程式可處理的媒體類型
| Accept-Charset| 優先的字元集
| Accept-Encoding| 優先的內容編碼
| Accept-Language| 優先的語言(自然語言)
| Authorization| Web 認證資訊
| Expect| 期待伺服器的特定行為
| From| 使用者的電子郵箱地址
| Host| 請求資源所在伺服器
| If-Match| 比較實體標記( ETag )
| If-Modified-Since| 比較資源的更新時間
| If-None-Match| 比較實體標記(與 If-Match 相反)
| If-Range| 資源未更新時發送實體 Byte 的範圍請求
| If-Unmodified-Since| 比較資源的更新時間(與 If-Modified-Since 相反)
| Max-Forwards| 最大傳輸逐跳數
| Proxy-Authorization| Proxy 伺服器要求用戶端的認證資訊
| Range| 實體的位元組範圍請求
| Referer| 對請求中 URI 的原始擷取方
| TE| 傳輸編碼的優先順序
| User-Agent| HTTP 用戶端程式的資訊

響應首部欄位( Response Header Fields )從伺服器端向用戶端返迴響應報文時使用的首部。補充了響應的附加內容,也會要求用戶端附加額外的內容資訊。
| 首部欄位名 | 說明
| ------------- |:-------------:
| Accept-Ranges| 是否接受位元組範圍請求
| Age| 推算資源建立經過時間
| ETag| 資源的匹配資訊
| Location| 令用戶端重新導向至指定 URI
| Proxy-Authenticate| Proxy 伺服器對用戶端的認證資訊
| Retry-After| 對再次發起請求的時機要求
| Server| HTTP 伺服器的安裝資訊
| Vary| Proxy 伺服器緩衝的管理資訊
| WWW-Authenticate| 伺服器對用戶端的認證資訊

實體首部欄位( Entity Header Fields )針對請求報文和響應報文的實體部分使用的首部。補充了資源內容更新時間等與實體有關的資訊。
| 首部欄位名 | 說明
| ------------- |:-------------:
| Allow| 資源可支援的 HTTP 方法
| Content-Encoding| 實體主體適用的編碼方式
| Content-Language| 實體主體的自然語言
| Content-Length| 實體主體的大小(單位:位元組)
| Content-Location| 替代對應資源的 URI
| Content-MD5| 實體主體的報文摘要
| Content-Range| 實體主體的位置範圍
| Content-Type| 實體主體的媒體類型
| Expires| 實體主體到期的日期時間
| Last-Modified| 資源的最後修改日期時間

HTTP 首部欄位將定義成緩衝代理和非緩衝代理的行為,分成 2 種類型
  1. 端到端首部( End-to-end Header )
    分在此類別中的首部會轉寄給請求 / 響應對應的最終接收目標,且必須儲存在由緩衝產生的響應中,另外規定它必須被轉寄。
  2. 逐跳首部( Hop-by-hop Header )
    分在此類別中的首部只對單次轉寄有效,會因通過緩衝或代理而不再轉寄。 HTTP/1.1 和之後版本中,如果要使用 hop-by-hop 首部,需提供 Connection 首部欄位。

下面列舉了 HTTP/1.1 中的逐跳首部欄位。除這 8 個首部欄位之外,其他所有欄位都屬於端到端首部

  • Connection
  • Keep-Alive
  • Proxy-Authenticate
  • Proxy-Authorization
  • Trailer
  • TE
  • Transfer-Encoding
  • Upgrade

關於各個首部命令的詳細參數可參見HTTP Headers

HTTP進階

如果這篇文章到上面為止,那跟手冊貌似沒有什麼區別,之前聽到過一位大神說的話,掌握一門技術,不僅需要知道這門技術能做到什麼,也要知道它不能做到什麼,在掌握一門技術的邊界之後,便對一門技術遊刃有餘了,畢竟技術那麼多,推陳換代那麼快,對於大多數人來說沒有精力貌似也不是十分必要去記憶技術的每一個細節(即使記憶了,有許多細節是我們平常用不到的,也就漸漸遺忘了)。在我們掌握邊界以後,遇到問題時,我們知道這個問題能依靠該項技術解決,至於怎麼解決,那時我們在去查相應手冊即可。

那對於本篇的HTTP來說,我們就從以下兩方面進行分析

HTTP能做到什麼

我們下面結合實際工作中的所遇到的HTTP的應用情境分為兩大方面

訪問大資料(圖片,視頻,大檔案)時

在訪問這些大資料時,我們往往會遇到以下問題
1. 如何請求部分資料?
2. 假設已請求了某資源的部分資料,程式終止了,伺服器更新了該資源,用戶端如何做,即已緩衝的資料是否可信?

我們通常在做下載功能的時候往往需要斷點續傳甚至多線程斷點續傳,那麼上面的問題就肯定會遇到。下面我們一一解答
1.使用Range來請求某個資源的部分資料

Range: bytes=bytes=200-1000, 2000-6576, 19000-表示請求擷取從第 200 位元組至第1000 ,2000-6576,19000之後所有位元組的資源。在一個  Range 首部中,可以一次性請求多個部分,伺服器會以 multipart 檔案的形式將其返回。如果伺服器返回的是範圍響應,需要使用 206 Partial Content 狀態代碼。假如所請求的範圍不合法,那麼伺服器會返回  416 Range Not Satisfiable 狀態代碼,表示用戶端錯誤。

2.ETag以及If-Match If-None-Match If-Range

響應首部欄位ETag 能告知用戶端實體標識。它是一種可將資源以字串形式做唯一性標識的方式。伺服器會為每份資源分派對應的 ETag值。另外,當資源更新時, ETag 值也需要更新。產生 ETag 值時,並沒有統一的演算法規則,而僅僅是由伺服器來分配。

ETag 中有強 ETag 值和弱 ETag 值之分。
強 ETag 值,不論實體發生多麼細微的變化都會改變其值。

ETag: "1234"

弱 ETag 值,只用於提示資源是否相同。只有資源發生了根本改變,產生差異時才會改變 ETag 值。這時,會在欄位值最開始處附加 W/

ETag: W/"1234"

請求首部欄位If-Match If-None-Match If-Range
形如 If-xxx 這種樣式的請求首部欄位,都可稱為條件請求。伺服器接收到附帶條件的請求後,只有判斷指定條件為真時,才會執行請求。
If-Match ,它會告知伺服器匹配資源所用的實體標記( ETag )值。這時的伺服器無法使用弱 ETag 值。伺服器會比對 比If-Match 的欄位值和資源的 ETag 值,僅當兩者一致時,才會執行請求。反之,則返回狀態代碼 412 Precondition Failed 的響應。還可以使用星號( * )指定 If-Match 的欄位值。針對這種情況,伺服器將會忽略 ETag 的值,只要資源存在就處理請求。

If-None-Match 只有在 If-None-Match 的欄位值與 ETag 值不一致時,可處理該請求。與 If-Match 首部欄位的作用相反

If-Range 瀏覽器告訴 WEB 伺服器,如果我請求的對象沒有改變,就把我缺少的部分給我,如果對象改變了,就把整個對象給我。瀏覽器通過發送請求對象的ETag 或者自己所知道的最後修改時間給 WEB 伺服器,讓其判斷對象是否改變了。總是跟 Range 頭部一起使用。

使用形式如下

If-Range: Wed, 21 Oct 2015 07:28:00 GMTRange: bytes=bytes=200-1000
If-Range: "1234"Range: bytes=bytes=200-1000

If-Range 頭欄位通常用於斷點續傳的下載過程中,用來自從上次中斷後,確保下載的資源沒有發生改變。

通常情況下的HTTP請求與響應

我們現在的伺服器大多是符合RESTFUL規範的,作為用戶端(網頁、Android、IOS)來說,我們與伺服器的通常互動是資料量比較小的操作,增刪改查,傳遞以及解析顯示JSON資料。這是我們通常使用HTTP的情境。這種情境下所常用的HTTP頭部欄位是包含上述訪問大資料(圖片,視頻,大檔案)時的請求欄位的,這些首部欄位各有含義,見HTTP Headers

HTTP不能做到什麼(缺陷)
  • 一條串連上只可發送一個請求。
  • 請求只能從用戶端開始。用戶端不可以接收除響應以外的指令。
  • 請求 / 響應首部未經壓縮就發送。首部資訊越多延遲越大。
  • 發送冗長的首部。每次互相發送相同的首部造成的浪費較多。

備忘:由於上述HTTP/1.1協議的缺陷,SPDYWebSocket 等協議紛紛出現,還有HTTP/2.0版本,這些協議的出現在一定程度上彌補了HTTP/1.1協議的缺陷。感興趣的同學可以深入瞭解。

本篇總結

本篇文章介紹了HTTP協議。從TCP/IP到HTTP以及HTTP進階,層層遞進,希望讀者有些許收貨。

此致,敬禮

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

Tags Index: