http協議及基於http協議的文件下載

目錄

1. HTTP 協議概述

2. URL 與資源

3. HTTP報文

4. 使用Postman 獲取數據

5. 基於HTTP協議的文件下載

5.1 文件整體下載

 5.2 文件分段(Range)下載

5.2.1 獲取文件的大小

5.2.2 下載分段文件

5.3 文件分塊(chunk)下載


1. HTTP 協議概述

日常我們使用網絡用得最多的無疑是在Web 瀏覽器(下文統一使用瀏覽器)上查找資料、看視頻、看書、看新聞等等,而在瀏覽器中只需要輸入一些搜索就可以得到想要的信息,這歸根於搜索引擎的好處,但是實際上每個網頁其實由多個資源組成。我們的瀏覽器就是一個HTTP 客戶端,通過HTTP 協議訪問服務器,得到服務器中的HTML 頁面、文本文件、圖片、音頻等資源,並且將這些資源搬運到瀏覽器(客戶端)顯示給我們。

HTTP 協議是Hyper Text Transfer Protocol(超文本傳輸協議)的縮寫,是用於從萬維網(WWW:World Wide Web )服務器傳輸超文本到本地瀏覽器的傳輸協議,它是基於TCP/IP 協議通信的,因此它也是基於<客戶端-服務器>模型運作的,是一個應用層協議,可以用它來傳輸服務器的各種資源,如文本、圖片、音頻等。

HTTP 協議的特點:

1. 簡單:當客戶端向服務器請求服務時,只需傳送請求方法和路徑即可獲取服務器的資源,請求方法常用的有GET、HEAD、POST 等,每種方法規定了客戶端與服務器通信的類型不同。

2. 快捷:由於HTTP 協議簡單,使得HTTP 服務器的程序規模小,因而通信速度很快。

3. 靈活:HTTP 允許傳輸任意類型的數據對象,傳輸的類型由Content-Type 加以標記。

4. 無連接:無連接的含義是限制每次連接只處理一個請求,服務器處理完客戶的請求,並收到客戶的應答後,即斷開連接,簡單來說就是每進行一次HTTP 通信,都要斷開一次TCP 連接,可隨着HTTP 的普及,文檔中包含大量圖片的情況多了起來,每次請求完都要斷開TCP 連接,無疑增加通信量的開銷,爲了解決TCP的連接問題,HTTP1.1 提出了持久連接的方法,即任意一端只要沒有明確提出斷開連接,則保持TCP 連接狀態,這樣子就就減少了TCP 連接的重複建立和斷開所造成的額外開銷,減輕了服務端的負載。注意,在HTTP1.1 版本之後纔出現持久連接的方法

5. 無狀態:HTTP 協議是無狀態協議,無狀態是指協議對於事務處理沒有記憶能力,即HTTP 協議無法根據之前的狀態進行本次的請求處理,這就意味着如果後續處理需要前面的信息,它必須重傳數據,這樣的情況可能導致HTTP 協議傳輸的數據量增大,當然,凡事都有兩面性,在另一方面,在服務器不需要先前信息時它的應答就較快,可以減少服務器的資源消耗,其實這種無狀態對於用戶來說也是不友好的,因此爲了解決無狀態的問題,引入了Cookie 技術,這是一種可以讓服務器知道用戶上一次做了什麼操作,並且記錄下來,它是存儲在客戶端之中的,比如我們在淘寶上買東西,我們選擇了幾個商品,但是到了結賬會跳轉到另一個頁面,此時如果服務器不知道我們選擇了哪些商品,那怎麼能結賬成功呢?所以Cookie 就是用來繞開HTTP 的無狀態性的“手段”之一,服務器可以設置或讀取Cookies 中包含信息,讓服務器知道我們選擇了什麼商品,藉此維護用戶跟服務器會話中的狀態,當然,Cookie 會被加密存儲在客戶端中,直到過期或者手動清除。

2. URL 與資源

我們可以把整個英特網看做是一個巨大的圖書館,裏面的資源應有盡有,並且是對我們是開放的,我們想要找一本書,那麼我們就需要直到他存放在哪裏,然後去找到它。網絡中的資源也是應有盡有,那麼怎麼樣才能在網絡的海洋中找到我們想要的資源呢?因此URI(Uniform Resource Identifiers)就被設計出來,用於統一管理資源,就像我們去圖書館找書一樣,我們必須通過圖書館的系統,找到書所在的位置,而不是讓我們自己一本一本書去找。

URL 全稱是Uniform Resource Locator,中文叫統一資源定位符,是互聯網上用來標識某一處資源的絕對地址,使用它我們就必然能找到資源,除非資源已經被轉移了。URI 是一個通用的概念,由兩個子集組成,分別是URL 和URN,URL 是通過資源的位置來標識資源,而URN 更高級一點,只需通過資源名字即可識別資源,與他們所處的位置是無關的,目前暫時還未推廣URN。

大部分URL 都會遵循URL 的語法,一個URL 的組成有多個不同的組件,一個URL的通用格式如下

<scheme>://<user>:<password>@<host>:<port>/<path>;<params>?<query>#<frag>

當然,絕大部分的URL 是不會包含所有組件的內容的.

URL 組件
scheme(方案) 指定訪問服務器獲取資源時使用哪種協議,有HTTP、HTTPS、FTP、SMTP 等協議。
user(用戶) 某些方案訪問資源時候需要指定用戶名,纔有權限獲取資源。
password(密碼) 用戶名後面可能需要密碼進行驗證,用戶名與密碼直接使用 “:” 冒號分隔連接。
host(主機) 資源宿主服務器的主機名或者IP 地址(點分十進制)。
port(端口) 資源宿主服務器正在監聽的端口號,很多方案都有默認的端口號,而無需我們自己填寫,比如HTTP 默認使用80 端口,HTTPS 默認使用443 端口。端口不是一個URL 必須的部分,如果省略端口部分,將採用默認端口。
path(路徑) 服務器本地資源的路徑。類似於電腦中的文件路徑一樣,使用“/”將路徑與端口隔離。從域名後的第一個“/”開始到最後一個“/”爲止,虛擬目錄也不是一個URL 必須的部分,在路徑之後是需要一個文件名,這就是URL 指定的資源。文件名部分也不是一個URL 必須的部分,如果省略該部分,則使用默認的文件名
params(參數) 某些方案會使用這個組件來輸入參數,可以擁有多個參數,使用“;”符號與路徑分隔開。
query(查詢) 某些方案會使用這個組件傳遞參數以激活應用程序,查詢組件的內容沒有通用的格式,用“?”字符與其他組件分隔開
frag(片段) 一小片或者一部分資源的名字,引用對象時,不會將片段組件內容傳輸給服務器,這個字段是在客戶端內部使用的,通過“#”字符與其他組件分隔開。

比如:https://blog.csdn.net/XieWinter,它就是一個URL,這是最簡單的一個URL 格式,通過其可以訪問相應的資源。

3. HTTP報文

HTTP 報文是由3 個部分組成,分別是:對報文進行描述的“起始行”,包含屬性的“首部”,以及可選的“數據主體”,對於請求報文與應答報文,只有“起始行”的格式是不一樣的。

http 請求報文

<method> <request-url> <version>                    // 起始行
<headers>                                           // 首部

<entity-body>                                       // 數據主體

http 應答報文

<version> <status> <reason-phrase>                  // 起始行
<headers>                                           // 首部

<entity-body>                                       // 數據主體

起始行和首部就是由行分隔的 ASCII 文本組成,每行都以由兩個字符組成的行終止序列作爲結束,其中包括一個回車符(ASCII 碼 13)和一個換行符(ASCII 碼 10), 這個行終止序列可以寫做 CRLF。

這兩種HTTP 報文的各個部分簡單描述:

  • 方法(method):HTTP 請求報文的起始行以方法作爲開始,方法用來告知服務器要做些什麼,常見的方法有GET、POST、HEAD 等,比如“GET /forum.php HTTP/1.1” 使用的就是GET 方法。
  • 請求URL(request-URL):指定了所請求的資源。
  • 版本(version):指定報文所使用的HTTP 協議版本,其中<major>指定了主要版本號, <minor>指定了次要版本號,它們都是整數,其格式如下:
HTTP/<major>.<minor>
  • 狀態碼(status):這是在HTTP 應答報文中使用的,狀態碼是在每條響應報文的起始行中返回的一個數字碼,描述了請求過程中所發送的情況,比如成功、失敗等,不同的狀態碼有不同的含義

200 常見的返回OK,404錯誤等,206 狀態碼將會文件分段下載中使用。

  • 原因短語(reason-phrase):這其實是給我們看的原因短語,因爲數字是不夠直觀,它只是狀態碼的一個文本形式表達而已。 
  • 首部(header):HTTP 報文可以有0 個、1 個或者多個首部,HTTP 首部字段向請求和響應報文中添加了一些附加信息,從本質上來說,它們是一個<名字:值>對,每個首部都包含一個名字,緊跟着一個冒號“:”,然後是一個可選的空格,接着是一個值,最後以CRLF 結束,比如“Host: blog.csdn.net”就是一個首部。
  • 數據主體(entity-body):這部分包含一個由任意數據組成的數據塊,其實這與我們前面所講的報文數據區域是一樣的,用於攜帶數據,HTTP 報文可以承載很多類型的數字數據:圖片、視頻、音頻、HTML 文檔、軟件應用程 序等。

4. 使用Postman 獲取數據

既然瞭解了HTTP 協議與HTTP 報文的相關知識,我們就來使用Postman 軟件瞭解一下HTTP 協議的傳輸過程。

官網:https://www.getpostman.com/

安裝後打開軟件,就可以使用軟件進行測試HTTP 協議,可以很直觀看到發送的HTTP 報文是什麼,也能很直觀看到響應的數據。

可以簡單註冊一下,就可以記錄發送的命令,相當不錯。

或者

查看發送HTTP 請求報文

  Postman 功能很強大,只是用到其最簡單的功能。

5. 基於HTTP協議的文件下載

5.1 文件整體下載

文件整體下載比較簡單,只需要知道資源的位置下載就可以。

 5.2 文件分段(Range)下載

在嵌入式設備或者資源有限的設備中,可能需要下載的文件的大小,自身資源不能一次性的滿足,需要分段下載纔行。文件分段下載的前提條件是,支持文件分段下載,否則,即便採用分段獲取的,下載的也是整個文件大小,比如GitHub上面的就會下載整個文件的大小。

文件下載的流程如下:

  • 獲取文件的大小
  • 下載各分段文件
  • 文件拼接
  • 文件完整性檢查

5.2.1 獲取文件的大小

因爲需要分段下載,因此,首先需要知道文件的大小,才方便計算分段,當然,不分段也不是不可以,但處理起來,沒有先獲取文件大小規範。

可以直接獲取資源鏈接的網站,檢查響應的消息頭字段中是否存在 如下字段,存在則支持,否則,不支持

Accept-Ranges: bytes

這就需要用到消息頭字段添加 Range 字段


Range: bytes=0-1024 獲取最前面1025個字節
Range: bytes=-500   獲取最後500個字節
Range: bytes=1025-  獲取從1025開始到文件末尾所有的字節
Range: 0-0          獲取第一個字節
Range: -1           獲取最後一個字節

請求成功後服務器會返回狀態碼206, 並返回如下字段指示返回結果, 0-1024指示返回分段範圍, 7877指示文件總大小
Content-Range: bytes 0-1024/7877

因此採用如下,獲取文件大小:

Range: 0-0          獲取第一個字節

示例:

5.2.2 下載分段文件

分段下載分爲兩種,一種就是一次請求一個分段,一種就是一次請求多個分段。

下面兩種都會有介紹,但是,實際上,除非爲了多線程多段下載(PC下載軟件 Internet Download Manager ),否則,在嵌入式開發中一般還是採用一次請求一個分段。

根5.2.1 可知文件大小,如示例文件大小爲 3700512 字節。

具體怎麼分段,根據設備資源來分配,原理是一樣都。

假如分三段 0-999999,1000000-1999999,2000000-3700511

一次請求一個分段

  • 第一段消息頭字段添加
Range: bytes=0-999999 獲取最前面1000000個字節

 

 

  • 第二段消息頭字段添加

Range: bytes=1000000-1999999 獲取第二個1000000個字節

 

 

  • 末段消息頭字段添加

Range: bytes=2000000-3700511 獲取最後一段
或另一種方式

Range: bytes=2000000- 獲取最後一段             // 這種更好些

 

 一次請求多個分段

 至此,文件下載完成,文件的拼接,如果是 類似都 xxx.bin文件,那麼保存的時候,同一塊地址連續保存就完成來拼接來。因爲這裏主要也是想表達通過http 服務分段下載文件,來升級程序,實現 FOTA 功能 ,其它方法暫不多說;

文件下載完成後,需要校驗文件都完整性,否則不完整都程序,一旦升級將造成死機的情況。

5.3 文件分塊(chunk)下載

Transfer-Encoding: chunked 表示輸出的內容長度不能確定,普通的靜態頁面、圖片之類的基本上都用不到這個。但動態頁面就有可能會用到,但也注意到大部分asp,php,asp.net動態頁面輸出的時候大部分還是使用Content-Length,沒有使用Transfer-Encoding: chunked。

Transfer-Encoding: chunked使用,就不必申請一個很大的字節數組了,可以一塊一塊的輸出,更科學,佔用資源更少。

這在http協議中也是個常見的字段,用於http傳送過程的分塊技術,原因是http服務器響應的報文長度經常是不可預測的,使用Content-length的實體搜捕並不是總是管用。分塊技術的意思是說,實體被分成許多的塊,也就是應用層的數據,TCP在傳送的過程中,不對它們做任何的解釋,而是把應用層產生數據全部理解成二進制流,然後按照MSS(Maxitum Segment Size 最大分段大小,一般客戶端資源有限,大小受其限制)的長度切成一分一分的,一股腦塞到tcp協議棧裏面去,而具體這些二進制的數據如何做解釋,需要應用層來完成,所以在這之前,一塊整體應用層的數據需要等它分成的所有TCP  segment到達對方,重新組裝後,應用程序才使用自己的解碼方法還原它們。

HTTP1.1採用了持久的連接,也就是一次TCP的連接不馬上釋放,允許許多的請求跟響應在一個TCP的連接上發送,所以客戶機與服務器需要某種方式來標示一個報文在哪裏結束和在下一個報文在哪裏開始。簡單的方法是使用呢content-length,但這隻有當報文長度可以預先判斷的時候才起作用,而對於動態的內容或者在發送數據前不能判定長度的情況下,可以使用分塊的方法來傳送編碼。

Web服務器有時生成HTTPResponse無法在Header就確定消息大小的,這時一般來說服務器將不會提供Content-Length的頭信息,而採用Chunked編碼動態的提供body內容的長度。進行Chunked編碼傳輸的HTTP Response會在消息頭部設置:Transfer-Encoding: chunked表示Content Body將用Chunked編碼傳輸內容。

 

 

 Chunked編碼使用若干個Chunk串連而成,由一個標明長度爲0的chunk標示結束。每個Chunk分爲頭部和正文兩部分,頭部內容指定下一段正文的字符總數(十六進制的數字)和數量單位(一般不寫),正文部分就是指定長度的實際內容,兩部分之間用回車換行(CRLF)隔開。在最後一個長度爲0的Chunk中的內容是稱爲footer的內容,是一些附加的Header信息(通常可以直接忽略)。

 

chunk編碼其實是一種動態數據傳輸協議,針對大數據可以動態傳輸,網頁可以動態顯示。

chunk編碼格式如下:

[chunk size][\r\n][chunk data][\r\n][chunk size][\r\n][chunk data][\r\n][chunk size = 0][\r\n][\r\n]

chunk size是以十六進制的ASCII碼錶示,比如33 37 35,計算成長度應該是:0x375(885),表示從回車之後有連續的885字節的數據。

chunk數據以0長度的chunk塊結束。

分塊傳輸的前提條件是,服務器支持該方式!!!且爲http1.1協議。

 

注:本節沒有詳細介紹HTTP及其消息頭字段,只是從FOTA都角度,來說明相關都東西,深入都需要查詢相關協議。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章