題圖:by Charles Loyerhtml
Hi,你們好,我是承香墨影!小程序
HTTP 協議在網絡知識中佔據了重要的地位,HTTP 協議最基礎的就是請求和響應的報文,而報文又是由報文頭(Header)和實體組成。大多數 HTTP 協議的使用方式,都是依賴設置不一樣的 HTTP 請求/響應 的 Header 來實現的。緩存
本系列《實用 HTTP》就拋開常規的 Header 講解式的表述方式,從實際問題出發,來分析這些 HTTP 協議的使用方式,究竟是爲了解決什麼問題?同時講解它是如何設計的和它實現原理。服務器
HTTP 協議是一種無狀態的「鬆散協議」,它不會記錄不一樣請求的狀態,而且由於它自己包含了兩端(客戶端和服務端),根據請求和響應來區分,它大部分的內容都只是一個建議,其實雙邊是能夠不遵照此建議的。網絡
「這裏寫了建議零售價 2 元...」「哦,不接受建議!」多線程
文本是本系列的第五篇,前四篇傳送門:工具
今天再來介紹一下 HTTP 的範圍請求。範圍請求主要是針對較大的文件的請求或者上傳,能夠僅操做它的某一段。佈局
一個比較常見的場景,就是斷點續傳/下載,在網絡狀況很差的時候,能夠在斷開鏈接之後,僅繼續獲取部份內容。例如在網上下載軟件,已經下載了 95% 了,此時網絡斷了,若是不支持範圍請求,那就只有被迫重頭開始下載。可是若是有範圍請求的加持,就只須要下載最後 5% 的資源,避免從新下載。學習
另外一個場景就是多線程下載,對大型文件,開啓多個線程,每一個線程下載其中的某一段,最後下載完成以後,在本地拼接成一個完整的文件,能夠更有效的利用資源。編碼
這算是兩個比較常見的場景,接下來咱們來看看範圍請求的 HTTP 協議支持的技術細節。
HTTP 自己是一種無狀態的「鬆散」協議,而在經歷了不少版本的迭代以後,只在 HTTP/1.1(RFC2616) 之上,才支持範圍請求。因此若是客戶端或者服務端兩端的某一端低於 HTTP/1.1,咱們就不該該使用範圍請求的功能。
而在 HTTP/1.1 中,很明確的聲明瞭一個響應頭部 Access-Ranges
來標記是否支持範圍請求,它只有一個可選參數 bytes
。
例如這裏給了一個 MP4 的響應頭,能夠看到它是有 Accept-Ranges:bytes
來標記的,有此標記標識當前資源支持範圍請求。
若是已經肯定雙端都支持範圍請求,咱們就能夠在請求資源的時候使用它。
全部的文件最終都是存儲在磁盤或者內存中的字節,對於待操做的文件能夠將其以字節爲單位分割。這樣只須要 HTTP 支持請求該文件從 n 到 n+x 這個範圍內的資源,就能夠實現範圍請求了。
HTTP/1.1 中定義了一個 Ranges 的請求頭,來指定請求實體的範圍。它的範圍取值是在 0 - Content-Length
之間,使用 -
分割。。
例如已經下載了 1000 bytes 的資源內容,想接着繼續下載以後的資源內容,只要在 HTTP 請求頭部,增長 Ranges:bytes=1000-
就能夠了。
Range 還有幾種不一樣的方式來限定範圍,能夠根據須要靈活定製:
1. 500-1000:指定開始和結束的範圍,通常用於多線程下載。
2. 500- :指定開始區間,一直傳遞到結束。這個就比較適用於斷點續傳、或者在線播放等等。
3. -500:無開始區間,只意思是須要最後 500 bytes 的內容實體。
4. 100-300,1000-3000:指定多個範圍,這種方式使用的場景不多,瞭解一下就行了。
HTTP 協議是一種雙邊協商的協議,既然請求頭部已經肯定是使用 Ranges 了,還有響應頭部中,也須要使用 Content-Ragne
這個響應頭來標記響應的實體內容範圍。
Content-Range
的格式也很清晰,首先標記它的單位是 bytes 而後標記當前傳遞的內容實體範圍和總長度。
Content-Range: bytes 100-999/1000
在這個例子中,會傳遞 100 ~ 999 範圍的內容實體,而該資源文件的總大小是 1000 bytes。而且此時的 HTTP 響應狀態碼爲 206 Partial Content
。
HTTP 206 Partial Content 成功狀態響應代碼表示請求已成功,而且主體包含所請求的數據區間,該數據區間是在請求的Range
首部指定的。有關 206 狀態碼的解釋能夠參考:https://developer.mozilla.org...
因此一個正常的流程應該以下圖所示:
注意這裏的每一個 HTTP 事務中的響應頭裏,都是會包含 Content-Length
的,只是它包含的是當前範圍請求響應的內容實體長度,而非此資源完整的長度。
到這裏基本上算是講清楚 HTTP 範圍請求的正確流程了,接下來看看一些特殊的狀況。
當咱們在一些下載工具中,下載大尺寸資源的時候,偶爾中間暫停過再從新下載,可能會碰見它又重頭開始下載的狀況。
這看似是 HTTP 的範圍請求失效了,可是實際上並不必定如此,極可能是由於請求的資源,在請求的這個過程當中,發生了改變。
假如你下載的過程當中,下載的源資源文件發生了變化,可是 URL 沒有改變,此時文件長度可能已經變化了(這是很是容易發現的),極端狀況下就算沒有長度沒有變化,你再繼續下載,極可能最終下載完成以後,沒法將下載的內容拼接成咱們須要的文件。
若是咱們須要從服務器上下載某個資源,必定要預防此資源可能發生的變更。在以前講 HTTP 緩存的時候講到,在 HTTP 協議中,能夠經過 ETag 或者 Last-Modified 來標識當前資源是否變化。
在 HTTP 的範圍請求中,也可使用這兩個字段來區分分段請求的資源,是否有修改過,只須要在請求頭中,將它放在 If-Range
這個請求報文頭中便可。If-Range
使用 ETag
或者 Last-Modified
兩個參數任意一個,原樣填入便可。
此時,若是兩次操做的都是同一個資源文件,就會繼續返回 206 狀態碼,開始後續的操做,反之則會返回 200 狀態碼,表示文件發生改變,要從頭下載。
須要注意的是 If-Range
須要和 Range
配合起來使用,不然會被服務端忽略。
再額外提一點,若是客戶端請求報文頭中,對 Range 填入的範圍錯誤,會返回 416 狀態碼。
HTTP 416 Range Not Satisfiable 錯誤狀態碼意味着服務器沒法處理所請求的數據區間。最多見的狀況是所請求的數據區間不在文件範圍以內,也就是說,Range
首部的值,雖然從語法上來講是沒問題的,可是從語義上來講卻沒有意義。有關 416 狀態碼,能夠參考:https://developer.mozilla.org...
前面介紹的概念,不少技術點其實描述的都是某一個請求片斷,接下來咱們以一個實際的例子來講明範圍請求的具體細節。
在這個例子中,我找了一個視頻的播放地址,直接在 Chrome 中進行播放。正常播放以後,再隨手拖動視頻進度,以後無操做讓其自動播放一段時間,來看看 HTTP 的事務報文。
簡單描述一下狀況,天然播放的時候,會首先想資源的 URL 發送請求,返回 200 的響應碼,能夠判斷出當前資源支持 Accept-Ranges
,接下來會去使用 Range
發送範圍請求,獲得的響應碼就是 206,並返回對應範圍的實體內容。而在每次拖動進度的時候,都會去從新發送一個範圍請求,依照拖動的進度來計算請求範圍。此處不存在資源被修改的狀況,因此不會出現從新請求下載的狀況。
就不一個一個對 HTTP 事務截圖了,大概抽象了一下流程,以下圖所示:
能夠看到,一次資源下載其實包含了不少次的請求過程,咱們須要站在全局的角度來看到它。
到這裏咱們就已經把 HTTP 範圍請求的整個流程都說明清楚了。
再從新整理一下關鍵點:
1. HTTP 範圍請求,須要 HTTP/1.1 及之上支持,若是雙端某一段低於此版本,則認爲不支持。
2. 經過響應頭中的 Accept-Ranges
來肯定是否支持範圍請求。
3. 經過在請求頭中添加 Range
這個請求頭,來指定請求的內容實體的字節範圍。
4. 在響應頭中,經過 Content-Range
來標識當前返回的內容實體範圍,並使用 Content-Length 來標識當前返回的內容實體範圍長度。
5. 在請求過程當中,能夠經過 If-Range
來區分資源文件是否變更,它的值來自 ETag 或者 Last-Modifled。若是資源文件有改動,會從新走下載流程。
再配一張流程圖,就更清晰了。
到此 HTTP 範圍請求的全部關鍵技術點,就已經講解清楚。範圍請求被用在諸如:斷點續傳、多線程下載等場景下,大部分 CDN 上的資源都是支持範圍請求的,具體你能在什麼場景下應用,就看你的想象力了。
還有什麼更多的想法,歡迎留言討論。
公衆號後臺回覆成長『 成長』,將會獲得我準備的學習資料,也能回覆『 加羣』,一塊兒學習進步;你還能回覆『 提問』,向我發起提問。
推薦閱讀:
Android P 適配經驗 | 技術創業選擇清單 | HTTP傳輸編碼 | 什麼正在消耗你? | HTTP 內容編碼 | 圖解 HTTP 緩存 | 聊聊 HTTP 的 Cookie | 輔助模式實戰 | Accessibility 輔助模式 | 小程序 Flex 佈局 | 好的 PR 讓你更靠譜 | 密碼管理之道