掀開斷點續傳那一層面紗(下載篇)

来源:http://www.cnblogs.com/phpstudy2015-6/archive/2017/05/09/6821478.html
-Advertisement-
Play Games

1、簡介 這一篇文章主要介紹的是http協議下載時的斷點續傳,詳細到各個步驟。主要步驟有:DNS查找、TCP三次握手、http請求發送、TCP協議數據傳輸、暫停後的狀態、繼續下載、TCP三次握手、http請求發送、數據傳輸、。。。、下載成功發送http響應信息、TCP四次握手斷開連接。 2、原理知識 ...


1、簡介

  這一篇文章主要介紹的是http協議下載時的斷點續傳,詳細到各個步驟。主要步驟有:DNS查找、TCP三次握手、http請求發送、TCP協議數據傳輸、暫停後的狀態、繼續下載、TCP三次握手、http請求發送、數據傳輸、。。。、下載成功發送http響應信息、TCP四次握手斷開連接。

2、原理知識

  2.1、問答問答

  問:什麼是斷點續傳?斷點續傳的原理是什麼?

  答:斷點續傳就是信號中斷後(掉線或關機等),下次能夠從上次的地方接著傳送(一般指下載或上傳),不支持斷點續傳就意味著下次下載或上傳必須從零開始。http協議中的斷點續傳是基於Http頭Range以及Content-Range。HTTP頭中一般斷點下載時才用到Range和Content-Range實體頭,Range用戶請求頭中,指定第一個位元組的位置和最後一個位元組的位置,如( Range:200-300或者Range:200- );Content-Range用於響應頭。通俗的來講就是文件大小為10,這次下載了3,被中斷了,下次繼續下載時則將指針移到3位置,從3開始下載,最終將整個文件下載下來。

  2.2、簡單http下載文件

  請求下載整個文件: 
  GET /test.rar HTTP/1.1 
  Connection: close 
  Host: 192.168.95.11
  Range: bytes=0-801 //一般請求下載整個文件是bytes=0- 或不用這個頭
  一般正常回應 :
  HTTP/1.1 200 OK 
  Content-Length: 801      
  Content-Type: application/octet-stream 
  Content-Range: bytes 0-800/801 //801:文件總大小

  2.3、重要的幾個頭

  響應頭:

  Content-type:Content-type 告訴瀏覽器文件的MIME 類型,這是非常重要的一個響應頭了,MIME種類繁多。很可能會在程式中漏掉一些MIME類型,表示全部為 content-type:application/octet-stream(位元組流)

  Content-Disposition:是 MIME 協議的擴展,MIME 協議指示 MIME 用戶代理如何顯示附加的文件。當 Internet Explorer 接收到頭時,它會激活文件下載對話框,它的文件名框自動填充了頭中指定的文件名。 嗯,就是這個頭喲,激活彈出提示下載框,一般這樣寫content-disposition:attachment; filename=name

  Content-Length:"Content-Length: 321" 就是告訴瀏覽器這個文件的大小是321位元組,其實我發現好像不設置這個頭,瀏覽器也能自己識別   Pragma Cache-control:把這2個頭都設置成public 告訴瀏覽器緩存,我一般設置cache-control:public

  Content-Range:欄位說明伺服器返回了文件的某個範圍及文件的總長度。這時Content-Length欄位就不是整個文件的大小了,而是對應文件這個範圍的位元組數,這一點一定要註意。一般格式,Content-Range: bytes 500-999/1000

  響應頭: 

  Range:可以請求實體的一個或者多個子範圍。

  例如:
  表示頭500個位元組:bytes=0-499
  表示第二個500位元組:bytes=500-999
  表示最後500個位元組:bytes=-500
  表示500位元組以後的範圍:bytes=500-  【下載斷點續傳(一般range格式為500-)】
  第一個和最後一個位元組:bytes=0-0,-1
  同時指定幾個範圍:bytes=500-600,601-999
  但是伺服器可以忽略此請求頭,如果無條件GET包含Range請求頭,響應會以狀態碼206(PartialContent)返回而不是以200(OK)。【206表示伺服器已經完成get的部分請求,即表示斷點續傳】

3、支持斷點續傳的文件下載類

類中含有註釋,這裡不再多解釋了

FileDownload.class.php

  1 <?PHP
  2 #文件下載(支持斷點續傳)
  3 class FileDownload
  4 {
  5     #下載速度
  6     private $_speed = 512;
  7 
  8     /**
  9     * @desc 下載文件
 10     *  
 11     * @param $file string 下載的文件路徑
 12     * @param $name string 保存文件時的文件名,不寫則最終下載文件預設為原文件名
 13     * @param $reload bool 是否使用斷點續傳方式下載
 14     */
 15     public function download($file, $name='', $reload=false)
 16     {
 17         if(file_exists($file))  #判斷文件是否存在
 18         {
 19             if($name == '')     #判斷命名參數是否存在
 20             {
 21                 $name = basename($file);    #採用原文件名進行存儲
 22             }
 23             $fHandle = fopen($file, 'rb');   #只讀方式打開;為移植性考慮,使用b標記打開文件(不同系統有不同換行符)
 24             $fileSize = filesize($file);    #文件大小
 25             $ranges = $this->getRange($fileSize);  #斷點續傳時,先查看下載的區間範圍
 26             header('cache-control:public');         #可以被任何緩存所緩存
 27             header('content-type:application/octet-stream');  #告訴瀏覽器響應的對象的類型(位元組流、瀏覽器預設使用下載方式處理)
 28             header('content-disposition:attachment; filename='.$name); #不打開此文件,刺激瀏覽器彈出下載視窗
 29             #判斷是否使用續傳方式進行下載
 30             #且請求頭ranges不能為null(為null表示第一次請求下載)
 31             if($reload && $ranges!=null)
 32             {
 33                 header('HTTP/1.1 206 Partial Content');     #發送自定義報文 206續傳狀態碼
 34                 header('Accept-Ranges:bytes');              #表明伺服器支持Range請求,所支持的單位是位元組
 35                 # 剩餘長度 
 36                 header(sprintf('content-length:%u',$ranges['end']-$ranges['start'])); 
 37                 # range信息 
 38                 header(sprintf('content-range:bytes %s-%s/%s', $ranges['start'], $ranges['end'], $fileSize));  
 39                 # fHandle指針跳到斷點位置 
 40                 fseek($fHandle, sprintf('%u', $ranges['start'])); 
 41             }
 42             else
 43             {
 44                 header('HTTP/1.1 200 OK'); 
 45                 header('content-length:'.$fileSize);
 46             }
 47             while(!feof($fHandle))
 48             { 
 49                 echo fread($fHandle, round($this->_speed*1024,0)); 
 50                 ob_flush();    #把數據從PHP的緩衝中釋放出來
 51                 //sleep(2); // 用於測試,減慢下載速度 
 52             } 
 53             ($fHandle!=null) && fclose($fHandle);
 54         }
 55         else
 56         {
 57             #沒文件
 58             header("HTTP/1.1 404 Not Found");
 59             return false;
 60         }
 61     }
 62 
 63     /**
 64     * @desc 獲取請求頭部range信息
 65     *
 66     * @param $fileSize int 該文件的大小
 67     *
 68     * @return array|null 返回range信息或者null
 69     */
 70     public function getRange($fileSize)
 71     {
 72         if(isset($_SERVER['HTTP_RANGE']) && !empty($_SERVER['HTTP_RANGE']))
 73         {
 74             #請求頭部range信息  Range: bytes=41078-\r\n
 75             $range = $_SERVER['HTTP_RANGE']; 
 76             $range = preg_replace('/[\s|,].*/', '', $range); 
 77             $range = explode('-', substr($range, 6));       #只需將41078-進行分割變成數組
 78             #斷點續傳頭部range信息都是為 4444- 這種形式 ,因此切割後形成的數組就只有兩個元素
 79             $range = array_combine(array('start','end'), $range); 
 80             if(empty($range['start']))
 81             { 
 82                 $range['start'] = 0; 
 83             } 
 84             if(empty($range['end']))
 85             { 
 86                 $range['end'] = $fileSize; 
 87             } 
 88             return $range; 
 89         }
 90         return null;    #第一次請求沒有range信息
 91     }
 92 
 93     /**
 94     * @desc 設置文件下載速度
 95     *
 96     * @param $speed int 下載速度
 97     */
 98     public function setSpeed($speed)
 99     { 
100         if(is_numeric($speed) && $speed>16 && $speed<4096)
101         { 
102             $this->_speed = $speed; 
103         } 
104     } 
105 
106 }
107 
108 ?>

4、測試並分析其中的步驟

  4.1、前提準備工作

  • 將上面類文件中第六行下載速度更改為10
  • 去掉上面類文件第51行的註釋,使它有延遲
  • 使用火狐瀏覽器進行下載測試
  • 使用Wireshark抓包工具進行抓包分析
  • test.php文件
1 <?php
2 include 'FileDownload.class.php';
3 $a=new FileDownload();
4 #不支持斷點續傳
5 #$b=$a->download('./aa.txt','bb.txt');  
6 #支持斷點續傳
7 #$b=$a->download('./aa.txt','bb.txt',1);    
8 ?>

  開始測試:

  4.2、測試支持斷點續傳下載

  執行步驟:

  1、打開抓包工具進行監控

  2、用火狐瀏覽器進行訪問,Enter下載

  

  3、確認下載

  4、中途暫停兩次,最後下載成功

成功下載!

  分析抓包:

  1、首先Enter,第一步當然是進行DNS查找啦。這裡就不展開講了,可以參考這裡的內容http://www.cnblogs.com/phpstudy2015-6/p/6810130.html#_label18

  2、拿到功能變數名稱對應的IP後,瀏覽器向伺服器80埠發起TCP的連接請求,請看下麵的抓包圖-1,一到三行尾TCP連接,即TCP三次握手。具體可以參考我寫的這篇文章http://www.cnblogs.com/phpstudy2015-6/p/6810130.html#_label2

 抓包圖-1

  3、TCP連接後,瀏覽器發起一個HTTP請求,即抓包圖-1中的第4行。下圖是該http GET請求。第一次請求不存在信息頭range

http請求圖 

  4、http請求後,開始TCP數據傳輸,請看上面的抓包圖-1,第5行後就開始有順序的進行tcp層數據傳輸(192.168.95.11Web主機連續發送兩次數據給192.168.95.10瀏覽器;瀏覽器接收並回應一次Web主機,告訴Web主機已經收到數據並且完整無誤,可以繼續傳輸!)

  5、此時暫停下載,。請看下麵的抓包圖-2,第72行的時候,暫停下載(即斷開與Web伺服器的連接)。因為這是突然斷開的,Web主機並不知道瀏覽器已經斷開了,所以還一直發送數據給瀏覽器(73~76),但是Web伺服器沒有收到瀏覽器的回應,最後它也不發數據,大家分手了。

  這個請求最後是沒有收到Web伺服器的http響應信息的。按照原本的請求是下載完整個文件後,Web才發送http響應消息的,但是瀏覽器突然單方面斷開,此時數據都沒傳送完,怎麼會給你相應消息呢!

抓包圖-2

  6、繼續下載。請看下圖的抓包圖-3。

  點擊繼續下載時,即再從新發送一個http請求給伺服器。

  第77~79行是TCP連接(三次握手)

  第80行為發送http請求信息

  請看下麵的http請求信息,這一次含有請求頭Range,這是Web重要機制。在暫停下載的時候,瀏覽器會記住已經已經接受的位元組數,待繼續下載的時候,在構建http請求信息的時候會增加這一個重要的請求頭信息。這也是支持斷點續傳的一個前提條件。

  瀏覽器攜帶Range頭信息請求Web伺服器,此時我們需要在代碼層對這個重要信息進行處理。即取出該位元組數出,然後在文件中定位指針,然後讀文件開始續傳。【這是斷點續傳應用中的邏輯關鍵】

抓包圖-3

  7、重覆暫停一次,在繼續下載,觀察對比。暫停兩次可以從抓包圖-1中最右邊可以看到兩個紅色的橫線。

  8、最後下載成功啦,此時Web伺服器會發送http響應信息給瀏覽器。

  第350行尾響應行

  看下麵的http響應圖,響應狀態碼為206

  用紅色線標記的是我們代碼中自定義的響應頭

 抓包圖-4

http響應圖

  9、TCP四次握手,端斷開連接。看上面的抓包圖-4

  第352~354是TCP斷開連接。四次握手為什麼是只有三次通訊呢?

  TCP斷開具體也可以參考我之前寫的文章。

  第一次,瀏覽器發送FIN包(表示要斷開)、ACK(確認序列號)。seq=361 

  第二、三次,Web伺服器接受到瀏覽器發來的包,並回覆FIN包(我也要要斷開)、ACK(確認序列號)。seq=174554、ack=362 【Web將瀏覽器發來的seq=361+1=362,轉變成ack=362發給瀏覽器,表示我已經知道了】【此時瀏覽器並一起發送seq=174554,告訴瀏覽器說我要關閉連接啦】

  第四次,瀏覽器回覆Web伺服器,ack=174555 【瀏覽器將Web伺服器發來的seq=174554+1,轉變換成ack=174555發給Web主機,表示我已經知道了】

   TCP一直說是四次握手斷開,我認為這應該是邏輯上的四次握手,從抓包上來看的話,第二、三次合併為一次通訊了。

  4.3、測試不支持斷點續傳下載

  執行步驟:

  1、打開抓包工具進行監控

  2、用火狐瀏覽器進行訪問,Enter下載

  3、暫停下載

  4、繼續下載。突然不行了,下載失敗!為什麼會這樣呢!下麵我們來分析分析

  抓包分析:

  1、TCP連接、http get請求無異常

 

  2、從抓包分析在斷開前都無任何異常

  3、繼續下載抓包分析

  TCP連接正常

  http請求信息,看上去是正常的,但是相對於我們所寫的程式就不對勁了。請求信息中含有Range請求頭,他需要的是數據該該Range範圍內的,而我們程式定義的是非斷點續傳,即每次訪問都是重寫下載,因此Web傳輸的數據對不上瀏覽器之前的數據,最終出錯啦!

 5、總結

  從學習OSI網路模型、TCP/IP網路模型到深入瞭解TCP傳輸、http協議、DNS查找、以及http URL訪問具體細節步驟,最後到這個HTTP協議應用--斷點續傳,收穫還是挺豐厚的。 以上是自己對斷點續傳的理解,以及做的相應測試,若有不對的地方,希望大家指出,好讓我改正改正。

 

(以上是自己的一些見解,若有不足或者錯誤的地方請各位指出)

 作者:那一葉隨風   http://www.cnblogs.com/phpstudy2015-6/

 原文地址:http://www.cnblogs.com/phpstudy2015-6/p/6821478.html 

 聲明:本博客文章為原創,只代表本人在工作學習中某一時間內總結的觀點或結論。轉載時請在文章頁面明顯位置給出原文鏈接

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • You are given a string, s, and a list of words, words, that are all of the same length. Find all starting indices of substring(s) in s that is a conca... ...
  • 資料庫連接池: 負責分配、管理和釋放資料庫連接,它允許應用程式重覆使用一個現有的資料庫連接,而再不是重新建立一個;釋放空閑時間超過最大空閑時間的資料庫連接來避免因為沒有釋放資料庫連接而引起的資料庫連接遺漏;資料庫連接池原理: 連接池基本的思想是在系統初始化的時候,將資料庫連接作為對象存儲在記憶體中,當 ...
  • 題目描述 如題,給定一棵有根多叉樹,請求出指定兩個點直接最近的公共祖先。 輸入輸出格式 輸入格式: 第一行包含三個正整數N、M、S,分別表示樹的結點個數、詢問的個數和樹根結點的序號。 接下來N-1行每行包含兩個正整數x、y,表示x結點和y結點之間有一條直接連接的邊(數據保證可以構成樹)。 接下來M行 ...
  • a='我很好' ####python3 預設的編碼為unicode###unicode>gb2312unicode_gb2312=a.encode('gb2312') ###因為預設是unicode所以不需要decode(),直接encode成想要轉換的編碼如gb2312print('我的gb231 ...
  • 預處理語句是由一系列和預處理相關的命令符組成的.預處理語句以#作為起始標記,其後緊跟預處理命令關鍵字,之後是空格,空格之後是預處理命令的內容.C++提供多種預處理功能,如巨集定義,文件包括,條件編譯等. #define 在這個教程的開頭我們已經提到了一種預處理指令: #define ,可以被用來生成巨集 ...
  • spring為開發者提供了一個名為spring boot devtools的模塊來使Spring Boot應用支持熱部署,提高開發者的開發效率,無需手動重啟Spring Boot應用。 devtools的原理 深層原理是使用了兩個ClassLoader,一個Classloader載入那些不會改變的類 ...
  • 1)CDATA部分用<![CDATA[和]]>來限定其界限,它們是字元數據的一種特殊形式,可用使用它們來囊括那些含有<、>,&之類字元的字元串,而不必將它們解釋為標記例如:<![CDATA[<]]>,另外需要註意的是CDATA部分不能包含字元串]]>。 2)處理指令(processing instr ...
  • 題目描述 最近,Elaxia和w的關係特別好,他們很想整天在一起,但是大學的學習太緊張了,他們 必須合理地安排兩個人在一起的時間。Elaxia和w每天都要奔波於宿舍和實驗室之間,他們 希望在節約時間的前提下,一起走的時間儘可能的長。 現在已知的是Elaxia和w**所在的宿舍和實驗室的編號以及學校的 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...