http斷點續傳原理
這周完成了一個斷點續傳的功能。
我們的遊戲裏加載地圖的邏輯簡化而言是這樣:
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斷點續傳原理