现在的位置: 首页 > 自动控制 > 工业·编程 > 正文

多线程断点续传研究(2)

2015-11-09 16:55 工业·编程 ⁄ 共 10498字 ⁄ 字号 暂无评论

    上篇文章《多线程断点续传研究(1) 》写完,由于整体思路是正确的,但是没有真正形成多线程下载,所以对本身的代码进行关键点的检查,尤其在一些操作web请求的地方,看看是否有什么问题,最后发现显示的关闭HttpWebResponse对象,能稍微有所改进。

那么修改后的类,大致代码如下:

//--------------------------- Download File class ---------------------------------------

//---------------------------------------------------------------------------------------

//---File: clsNewDownloadFile

//---Description: The multi-download file class file using HttpWebRequest class

//---Author: Knight

//---Date: Aug.4, 2006

//---------------------------------------------------------------------------------------

//---------------------------{Download File class}---------------------------------------

namespace DownloadFile

{

using System;

using System.IO;

using System.Net;

using System.Text;

using System.Security;

using System.Threading;

using System.Collections.Specialized;

using System.Diagnostics;

using System.Data;

/// <summary>

/// Download file info

/// </summary>

public class DownloadFileInfo

{

private string _RequestURL;

private string _ResponseURL;

private string _FileName;

private int _TotalSize;

private int _BlockLeftSize;

public string RequestURL

{

get{ return _RequestURL; }

}

public string ResponseURL

{

get{ return _ResponseURL; }

}

public string FileName

{

get{ return _FileName; }

}

public int TotalSize

{

get{ return _TotalSize;}

set{ _TotalSize = value;}

}

public int BlockLeftSize

{

get{ return _BlockLeftSize;}

set{ _BlockLeftSize = value;}

}

public DownloadFileInfo(

string RequestURL,

string ResponseURL,

string FileName,

int TotalSize,

int BlockLeftSize )

{

this._RequestURL = RequestURL;

this._ResponseURL = ResponseURL;

this._FileName = FileName;

this._TotalSize = TotalSize;

this._BlockLeftSize = BlockLeftSize;

}

public DownloadFileInfo(

string RequestURL,

string ResponseURL,

string FileName ):

this( RequestURL, ResponseURL, FileName, 0, 0 )

{

}

}

/// <summary>

/// Download event arguments

/// </summary>

public class DownLoadEventArgs : System.EventArgs

{

private DownloadFileInfo _DownloadFileInfo;

private int _Position;

private int _ReadCount;

private int _ThreadNO;

public DownloadFileInfo DownFileInfo

{

get{ return _DownloadFileInfo; }

}

public int StartPosition

{

get{ return _Position; }

}

public int ReadCount

{

get{ return _ReadCount; }

}

public int ThreadNO

{

get{ return _ThreadNO; }

}

public DownLoadEventArgs(

DownloadFileInfo DownFileInfo,

int nStartPostion,

int nReadCount,

int nThreadNO )

{

this._DownloadFileInfo = DownFileInfo;

this._Position = nStartPostion;

this._ReadCount = nReadCount;

this._ThreadNO = nThreadNO;

}

}

public delegate void DataReceivedEventHandler( DownLoadEventArgs e );

public delegate void ThreadFinishedHandler();

/// <summary>

/// class for sub-download threads

/// </summary>

public class SubDownloadThread

{

private readonly DownloadFileInfo _FileInfo;

private int _ThreadNO;

private int _Position;

private int _BlockSizeLeft;

public event DataReceivedEventHandler DataReceived;

private ThreadFinishedHandler _Finished;

private bool _IsStopped;

private Thread thdDownload;

public SubDownloadThread(

DownloadFileInfo DownFileInfo,

int ThreadNO,

int Position,

int BlockSizeLeft,

ThreadFinishedHandler Finished )

{

this._FileInfo = DownFileInfo;

this._ThreadNO = ThreadNO;

this._Position = Position;

this._BlockSizeLeft = BlockSizeLeft;

this._Finished = Finished;

}

/// <summary>

/// Start to create thread to download file

/// </summary>

public void StartDownload()

{

thdDownload = new Thread( new ThreadStart( this.Download ) );

thdDownload.Start();

}

/// <summary>

/// Stop current download-thread

/// </summary>

public void Stop()

{

_IsStopped = true;

if( thdDownload.ThreadState == System.Threading.ThreadState.Running )

thdDownload.Join( 10 );

Debug.WriteLine( string.Format( "Thread NO:{0} is stopped!" ,_ThreadNO ) );

}

/// <summary>

/// Function for sub-thread

/// </summary>

private void Download()

{

HttpWebResponse hwrp = null;

try

{

Debug.WriteLine( string.Format( "Thread NO:{0} begins to download!" ,_ThreadNO ) );

// Creat request by url

HttpWebRequest hwrq = (HttpWebRequest) WebRequest.Create(

new Uri( this._FileInfo.RequestURL ));

// Go to download position

hwrq.AddRange( _Position );

// Get response from request

hwrp = (HttpWebResponse) hwrq.GetResponse();

}

catch (Exception e)

{

Debug.WriteLine( e.Message );

return;

}

// download and write data from

Debug.WriteLine( string.Format( "Thread NO:{0} call function named DownloadData!" ,

_ThreadNO ) );

DownloadData( hwrp );

hwrp.Close();//Close response here

}

/// <summary>

/// Download data through web request

/// </summary>

/// <param name="Response"></param>

private void DownloadData( WebResponse Response )

{

const int BUFFER_SIZE = 0x10000;//64k buffer size

byte[] bBuffer = new byte[ BUFFER_SIZE ];

Stream sReader = Response.GetResponseStream();

if( sReader == null ) return;

int nReadCount = 0;

while( !_IsStopped && this._BlockSizeLeft > 0 )

{

Debug.WriteLine( string.Format( "Thread NO:{0} reads data!" ,

_ThreadNO ) );

// Set read size

nReadCount = ( BUFFER_SIZE > this._BlockSizeLeft )

? this._BlockSizeLeft:BUFFER_SIZE ;//Get current read count

// Read data

int nRealReadCount = sReader.Read( bBuffer, 0, nReadCount );

if( nRealReadCount <= 0 ) break;

Debug.WriteLine( string.Format( "Thread NO:{0} writes data!" ,

_ThreadNO ) );

// Write data

using( FileStream sw = new FileStream(

this._FileInfo.FileName,

System.IO.FileMode.OpenOrCreate,

System.IO.FileAccess.ReadWrite, System.IO.FileShare.ReadWrite))

{

sw.Position = this._Position;

sw.Write( bBuffer, 0, nRealReadCount );

Debug.WriteLine( string.Format( "Thread NO:{0} writes {1} data!" ,

_ThreadNO, nRealReadCount ) );

sw.Flush();

sw.Close();

}

Debug.WriteLine( string.Format( "Thread NO:{0} send callback info!" ,

_ThreadNO ) );

// Call back

DataReceived( new DownLoadEventArgs( this._FileInfo,

this._Position,

nRealReadCount,

this._ThreadNO ) );

// Set position and block left

this._Position += nRealReadCount;

this._BlockSizeLeft -= nRealReadCount;

}

if( _IsStopped ) return;

Debug.WriteLine( string.Format( "Thread NO:{0} sends finish-info!" ,

_ThreadNO ) );

_Finished();

}

}

/// <summary>

/// Class for download thread

/// </summary>

public class DownloadThread

{

private DownloadFileInfo _FileInfo;

private SubDownloadThread[] subThreads;

private int nThreadFinishedCount;

private DataRow drFileInfo;

private DataTable dtThreadInfo;

public DataReceivedEventHandler DataReceived;

public event ThreadFinishedHandler Finished;

/// Class's Constructor

public DownloadThread(

string RequestURL,

string ResponseURL,

string FileName,

DataRow NewFileInfo,

int SubThreadNum )

{

// Create download file info

_FileInfo = new DownloadFileInfo( RequestURL, ResponseURL, FileName );

// Create request to get file info

bool blnMultiDownload = GetFileDownInfo();

// Create file info datarow

drFileInfo = NewFileInfo;

InitDataRow();

// Create subthreads and set these info in threadinfo table

if( SubThreadNum <= 0 ) SubThreadNum = 5;//Defualt value

//Not support to be multi-thread download or less than 64k

if( !blnMultiDownload || _FileInfo.TotalSize <= 0x10000 ) SubThreadNum = 1;

subThreads = new SubDownloadThread[SubThreadNum];

nThreadFinishedCount = 0;

CreateThreadTable( SubThreadNum );

}

public DownloadThread( DataRow DownloadFileInfo, DataTable ThreadInfos )

{

// Create download file info

drFileInfo = DownloadFileInfo;

_FileInfo = new DownloadFileInfo(

drFileInfo["RequestURL"].ToString(),

drFileInfo["ResponseURL"].ToString(),

drFileInfo["FileName"].ToString(),

Convert.ToInt32( drFileInfo["TotalSize"].ToString() ),

Convert.ToInt32( drFileInfo["BlockLeftSize"].ToString() ) );

// Create sub thread class objects

dtThreadInfo = ThreadInfos;

subThreads = new SubDownloadThread[ dtThreadInfo.Rows.Count ];

nThreadFinishedCount = 0;

}

/// {Class's Constructor}

/// <summary>

/// Start to download

/// </summary>

public void Start()

{

StartSubThreads();

}

/// <summary>

/// Create table to save thread info

/// </summary>

/// <param name="SubThreadNum"></param>

private void CreateThreadTable( int SubThreadNum )

{

int nThunkSize = _FileInfo.TotalSize / SubThreadNum;

dtThreadInfo = new DataTable("DownloadFileInfo");

dtThreadInfo.Columns.Add( "ThreadNO", typeof(int) );

dtThreadInfo.Columns.Add( "Position", typeof(int) );

dtThreadInfo.Columns.Add( "BlockLeftSize", typeof(int) );

DataRow drNew;

int i = 0;

for( ; i < SubThreadNum-1; i++ )

{

drNew = dtThreadInfo.NewRow();

drNew["ThreadNO"] = i;

drNew["Position"] = i * nThunkSize;

drNew["BlockLeftSize"] = nThunkSize;

dtThreadInfo.Rows.Add( drNew );

}

drNew = dtThreadInfo.NewRow();

drNew["ThreadNO"] = i;

drNew["Position"] = i * nThunkSize;

drNew["BlockLeftSize"] = _FileInfo.TotalSize - i * nThunkSize;

dtThreadInfo.Rows.Add( drNew );

dtThreadInfo.AcceptChanges();

}

/// <summary>

/// Start sub-threads to download file

/// </summary>

private void StartSubThreads()

{

foreach( DataRow dr in dtThreadInfo.Rows )

{

int ThreadNO = Convert.ToInt32( dr["ThreadNO"].ToString() );

if( Convert.ToInt32( dr["BlockLeftSize"].ToString() ) > 0 )

{

subThreads[ ThreadNO ] = new SubDownloadThread(

_FileInfo,

ThreadNO,

Convert.ToInt32( dr["Position"].ToString() ),

Convert.ToInt32( dr["BlockLeftSize"].ToString() ),

new ThreadFinishedHandler( this.DownloadFinished ) );

subThreads[ ThreadNO ].DataReceived += this.DataReceived;

subThreads[ ThreadNO ].StartDownload();

}

else

DownloadFinished();

}

}

/// <summary>

/// Save download file info

/// </summary>

private void InitDataRow()

{

drFileInfo.BeginEdit();

drFileInfo["RequestURL"] = _FileInfo.RequestURL;

drFileInfo["ResponseURL"] = _FileInfo.ResponseURL;

drFileInfo["FileName"] = _FileInfo.FileName;

drFileInfo["TotalSize"] = _FileInfo.TotalSize;

drFileInfo["BlockLeftSize"] = _FileInfo.BlockLeftSize;

drFileInfo["CreatedTime"] = DateTime.Now.ToString( "yyyyMMddHHmmss" );

drFileInfo.EndEdit();

drFileInfo.AcceptChanges();

}

/// <summary>

/// Check url to get download file info

/// </summary>

/// <returns></returns>

private bool GetFileDownInfo()

{

HttpWebRequest hwrq;

HttpWebResponse hwrp = null;

try

{

//Create request

hwrq = (HttpWebRequest) WebRequest.Create( _FileInfo.RequestURL );

hwrp = (HttpWebResponse) hwrq.GetResponse();

//Get file size info

long L = hwrp.ContentLength;

L = ((L == -1) || (L > 0x7fffffff)) ? ((long) 0x7fffffff) : L;

_FileInfo.TotalSize = (int)L;

_FileInfo.BlockLeftSize = _FileInfo.TotalSize;

//Check whether this url is supported to be multi-threads download

bool blnMulti = ( hwrp.Headers["Accept-Ranges"] != null

&& hwrp.Headers["Accept-Ranges"] == "bytes");

hwrp.Close();//Close response here

return blnMulti;

}

catch (Exception e)

{

throw e;

}

}

/// <summary>

/// Update download thread info

/// </summary>

/// <param name="ThreadNO"></param>

/// <param name="Position"></param>

/// <param name="DownloadSize"></param>

public void UpdateDownloadInfo( int ThreadNO, int Position, int DownloadSize )

{

if( ThreadNO >= dtThreadInfo.Rows.Count ) return;

DataRow drThreadNO = dtThreadInfo.Rows[ThreadNO];

drThreadNO["Position"] = Position + DownloadSize;

drThreadNO["BlockLeftSize"] = Convert.ToInt32( drThreadNO["BlockLeftSize"].ToString() )

- DownloadSize;

drThreadNO.AcceptChanges();

drFileInfo["BlockLeftSize"] = Convert.ToInt32( drFileInfo["BlockLeftSize"].ToString() )

- DownloadSize;

}

/// <summary>

/// Stop download threads

/// </summary>

public void Stop()

{

for( int i = 0; i < subThreads.Length; i++ )

subThreads[i].Stop();

}

/// <summary>

/// Thread download finished

/// </summary>

private void DownloadFinished()

{

// Some download thread finished

nThreadFinishedCount++;

if( nThreadFinishedCount == subThreads.Length )

{

// All download threads finished

drFileInfo.Delete();

//drFileInfo.AcceptChanges();

Finished();

}

}

public DataTable ThreadInfo

{

get{ return dtThreadInfo; }

}

}

}

不过对于这个类来说,我也只实现了两个线程同时下载,当初始化为3个以上线程的时候,就有等待线程出现,这一点和我用FlashGet看到现象一样,那么估计要支持多个线程,服务器需要做些设置,不过这些还需要进一步的验证。

作者:knight94

给我留言

留言无头像?