1. 程式人生 > >http斷點續傳原理

http斷點續傳原理

位置 解壓 md5 完成 不存在 狀態 失敗 來看 ID

這周完成了一個斷點續傳的功能。

我們的遊戲裏加載地圖的邏輯簡化而言是這樣:

1.首先用本地的md5文件校驗地圖文件(很多文件)是否完整。(中間有很多步驟,任何步驟失敗都認為地圖不完整)

2.如果完整,直接加載地圖。

3.如果不完整,需要通過一個http協議請求後臺服務器傳回完整的地圖。

現在要增加一個斷點續傳的功能。也就是地圖下載過程中,如果斷網,丟包之類,重啟遊戲地圖會繼續下載,而不是重新下載。給服務器減少流量。

要解決這個問題,要了解以下問題:

1.正常的http請求流程。

2.http分段請求的原理。

3.具體地圖下載流程的處理。

1.http請求的流程 

技術分享圖片

客戶端發送的請求被封裝成請求報文

請求報文的內容裏面包括請求方法,請求地址,http協議版本,請求首部。其中的請求方法就是所謂的Get,Post,Put,Header,Delete,Options,Trace,Connect這些。這裏我們無需詳細了解。

服務器收到請求以後,會以響應報文的形式發回客戶端。響應報文裏面包括了http協議版本,狀態碼,狀態碼的原因短語,響應首部,請求的實體主體

狀態碼會標記服務端是否正常處理,是個重要的參數。

技術分享圖片

我們來看下載地圖的請求。

因為是需要直接請求實體,當然是用Get方法。另外只需要知道Url就行。網絡正常的情況下,服務端會返回狀態碼200。而返回的實體就是地圖文件,通常是一個壓縮文件.zip,用二進制模式寫到一個文件,再用ziplib之類解壓。

2.http分段請求的原理

那麽斷點續傳怎麽做呢。在客戶端發起的請求報文裏,請求首部裏面有個可選參數Range,用來標識只請求實體的一部分。

格式形如:Range: bytes=500-999,請求500bytes到999bytes。Range: bytes=500-,請求500bytes開始到結束的部分。

那麽斷點續傳其實就是知道上一次傳輸的終點,把它作為Range參數的起點,發起請求。

如果請求首部帶有Range參數,那麽服務器正常情況會返回狀態碼206和請求的部分實體。

技術分享圖片

3.具體地圖下載流程的處理

地圖下載的具體邏輯。我這裏這樣處理:

服務器返回的實體內容寫到一個臨時文件,命名為temp.zip。如果下載完整了,就會把名字改掉,改為map_id.zip。如果不完整就不會改名了。

所以每次發起請求前,判斷是否需要斷點續傳,只需要判斷存不存在temp.zip即可。

1.是否存在temp.zip文件。

2.如果存在,請求頭部帶有Range參數,起點是temp文件的大小。

3.如果不存在,默認的請求頭部,請求完整的文件。

中間有一些細節問題。可能和具體的實現框架有關。想象一次研發測試流程:

1.啟動遊戲,第一次下載是一次完整下載,下載的過程中斷網(下載過程有個回調,回調參數有標識進度的process,在這裏打斷點,然後斷網),把收到的實體(應該是一部分)寫到temp.zip。

2.把網接回,再啟動遊戲,判斷是否有temp.zip,有temp.zip,所以將temp.zip的文件大小作為Range參數的起點,發起部分請求。把收到的實體寫入temp.zip,但是這次的寫入起點是文件的末尾。把文件改名為map_id.zip。

這兩個情況都是寫在一個函數裏的,那麽怎麽判斷temp.zip文件是否是完整的地圖文件呢?要不要改名了呢?

我這邊的框架裏面,服務器返回的實體res裏面有個error參數,通過這個參數就能知道本次返回的實體,是不是客戶端請求的全部實體(包括完整的請求和部分的請求)。

也就是說如果res裏面的error是true,那麽最後是不會走到改名那步的。下一次請求還是會走斷點續傳。反之error是false,那麽說明本次請求返回的實體已經完整,最後會走到改名那步。

另外一個問題。如果服務器上的文件內容變更了,我們還從舊文件的斷點位置請求,這樣就會出錯了。

這個問題,可以考慮請求首部的If-Range參數和響應首部的ETAG參數。也就是說要客戶端記錄傳回文件的ETAG,下一次請求把此ETAG作為If-Range參數。那麽服務器會根據ETAG判斷文件是否更新。

如果有更新,會走全量下載,返回狀態碼200。如果沒有更新,會走部分下載,返回狀態碼206。

引用:

1.《圖解http》

http斷點續傳原理