這周完成了一個斷點續傳的功能。服務器
咱們的遊戲里加載地圖的邏輯簡化而言是這樣:網絡
1.首先用本地的md5文件校驗地圖文件(不少文件)是否完整。(中間有不少步驟,任何步驟失敗都認爲地圖不完整)框架
2.若是完整,直接加載地圖。函數
3.若是不完整,須要經過一個http協議請求後臺服務器傳回完整的地圖。測試
如今要增長一個斷點續傳的功能。也就是地圖下載過程當中,若是斷網,丟包之類,重啓遊戲地圖會繼續下載,而不是從新下載。給服務器減小流量。blog
要解決這個問題,要了解如下問題:遊戲
1.正常的http請求流程。ip
2.http分段請求的原理。md5
3.具體地圖下載流程的處理。io
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.首先用本地的md5文件校驗地圖文件(不少文件)是否完整。(中間有不少步驟,任何步驟失敗都認爲地圖不完整)
2.若是完整直接使用本地地圖。
3.若是不完整,判斷是否存在temp.zip文件。
4.若是存在,請求頭部帶有Range參數,起點是temp文件的大小。
5.若是不存在,默認的請求頭部,請求完整的文件。
中間有一些細節問題。可能和具體的實現框架有關。想象一次研發測試流程:
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。
也能夠用其餘方法,好比你們約定好,服務器那邊地圖改變了,地圖就換一個名字(用時間戳作地圖名字後綴),那麼客戶端這邊請求的名字就會變了。不須要考慮If-Range參數。也沒必要在客戶端記錄ETAG。
引用:
1.《圖解http》