




已阅读5页,还剩13页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
断点续传和多线程下载编程在当今的网络时代,下载软件是使用最为频繁的软件之一。几年来,下载技术也在不停地发展。最原始的下载功能仅仅是个“下载”过程,即从WEB服务器上连续地读取文件。其最大的问题是,由于网络的不稳定性,一旦连接断开使得下载过程中断,就不得不全部从头再来一次。 随后,“断点续传”的概念就出来了,顾名思义,就是如果下载中断,在重新建立连接后,跳过已经下载的部分,而只下载还没有下载的部分。无论“多线程下载”技术是否洪以容先生的发明,洪以容使得这项技术得到前所未有的关注是不争的事实。在“网络蚂蚁”软件流行开后,许多下载软件也都纷纷效仿,是否具有多线程下载技术、甚至能支持多少个下载线程都成了人们评测下载软件的要素。多线程下载的基础是WEB服务器支持远程的随机读取,也即支持断点续传。这样,在下载时可以把文件分成若干部分,每一部分创建一个下载线程进行下载。现在,不要说编写专门的下载软件,在自己编写的软件中,加入下载功能有时也非常必要。如让自己的软件支持自动在线升级,或者在软件中自动下载新的数据进行数据更新,这都是很有用、而且很实用的功能。本文的主题即怎样编写一个支持断点续传和多线程的下载模块。当然,下载的过程非常复杂,在一篇文章中难以全部阐明,所以,与下载过程关系不直接的部分基本上都忽略了,如异常处理和网络错误处理等,敬请各位读者注意。我使用的开发环境是C+ Builder 5.0,使用其他开发环境或者编程语言的朋友请自行作适当修改。HTTP协议简介下载文件是电脑与WEB服务器交互的过程,它们交互的语言的专业名称是协议。传送文件的协议有多种,最常用的是HTTP(超文本传输协议)和FTP(文件传送协议),我采用的是HTTP。HTTP协议最基本的命令只有三条:Get、Post和Head。Get从WEB服务器请求一个特定的对象,比如HTML页面或者一个文件,WEB服务器通过一个Socket连接发送此对象作为响应;Head命令使服务器给出此对象的基本描述,比如对象的类型、大小和更新时间。Post命令用于向WEB服务器发送数据,通常使把信息发送给一个单独的应用程序,经处理生成动态的结果返回给浏览器。下载即是通过Get命令实现。基本的下载过程编写下载程序,可以直接使用Socket函数,但是这要求开发人员理解、熟悉TCP/IP协议。为了简化Internet客户端软件的开发,Windows提供了一套WinInet API,对常用的网络协议进行了封装,把开发Internet软件的门槛大大降低了。我们需要使用的WinInet API函数如图1所示,调用顺序基本上是从上到下,其具体的函数原型请参考MSDN。图1在使用这些函数时,必须严格区分它们使用的句柄。这些句柄的类型是一样的,都是HINTERNET,但是作用不同,这一点非常让人迷惑。按照这些句柄的产生顺序和调用关系,可以分为三个级别,下一级的句柄由上一级的句柄得到。InternetOpen是最先调用的函数,它返回的HINTERNET句柄级别最高,我习惯定义为hSession,即会话句柄。InternetConnect使用hSession句柄,返回的是http连接句柄,我把它定义为hConnect。HttpOpenRequest使用hConnect句柄,返回的句柄是http请求句柄,定义为hRequest。HttpSendRequest、HttpQueryInfo、InternetSetFilePointer和InternetReadFile都使用HttpOpenRequest返回的句柄,即hRequest。当这几个句柄不再使用是,应该用函数InternetCloseHandle把它关闭,以释放其占用的资源。首先建立一个名为THttpGetThread、创建后自动挂起的线程模块,我希望线程在完成后自动销毁,所以在构造函数中设置:FreeOnTerminate = True; / 自动删除并增加以下成员变量:char BufferHTTPGET_BUFFER_MAX+4; / 数据缓冲区AnsiString FURL; / 下载对象的URLAnsiString FOutFileName; / 保存的路径和名称HINTERNET FhSession; / 会话句柄HINTERNET FhConnect; / http连接句柄HINTERNET FhRequest; / http请求句柄bool FSuccess; / 下载是否成功int iFileHandle; / 输出文件的句柄 1、建立连接按照功能划分,下载过程可以分为4部分,即建立连接、读取待下载文件的信息并分析、下载文件和释放占用的资源。建立连接的函数如下,其中ParseURL的作用是从下载URL地址中取得主机名称和下载的文件的WEB路径,DoOnStatusText用于输出当前的状态:/初始化下载环境void THttpGetThread:StartHttpGet(void)AnsiString HostName,FileName;ParseURL(HostName, FileName); try/ 1.建立会话FhSession = InternetOpen(http-get-demo,INTERNET_OPEN_TYPE_PRECONFIG,NULL,NULL,0); / 同步方式if( FhSession=NULL)throw(Exception(Error:InterOpen);DoOnStatusText(ok:InterOpen);/ 2.建立连接FhConnect=InternetConnect(FhSession,HostName.c_str(),INTERNET_DEFAULT_HTTP_PORT,NULL,NULL,INTERNET_SERVICE_HTTP, 0, 0);if(FhConnect=NULL)throw(Exception(Error:InternetConnect);DoOnStatusText(ok:InternetConnect);/ 3.初始化下载请求const char *FAcceptTypes = */*;FhRequest = HttpOpenRequest(FhConnect,GET, / 从服务器获取数据FileName.c_str(), / 想读取的文件的名称HTTP/1.1, / 使用的协议NULL,&FAcceptTypes,INTERNET_FLAG_RELOAD,0);if( FhRequest=NULL)throw(Exception(Error:HttpOpenRequest);DoOnStatusText(ok:HttpOpenRequest);/ 4.发送下载请求HttpSendRequest(FhRequest, NULL, 0, NULL, 0);DoOnStatusText(ok:HttpSendRequest);catch(Exception &exception)EndHttpGet(); / 关闭连接,释放资源DoOnStatusText(exception.Message);/ 从URL中提取主机名称和下载文件路径void THttpGetThread:ParseURL(AnsiString &HostName,AnsiString &FileName)AnsiString URL=FURL;int i=URL.Pos(http:/);if(i0)URL.Delete(1, 7);i=URL.Pos(/);HostName = URL.SubString(1, i-1);FileName = URL.SubString(i, URL.Length();可以看到,程序按照图1中的顺序,依次调用InternetOpen、InternetConnect、HttpOpenRequest函数得到3个相关的句柄,然后通过HttpSendRequest函数把下载的请求发送给WEB服务器。 InternetOpen的第一个参数是无关的,最后一个参数如果设置为INTERNET_FLAG_ASYNC,则将建立异步连接,这很有实际意义,考虑到本文的复杂程度,我没有采用。但是对于需要更高下载要求的读者,强烈建议采用异步方式。HttpOpenRequest打开一个请求句柄,命令是GET,表示下载文件,使用的协议是HTTP/1.1。另外一个需要注意的地方是HttpOpenRequest的参数FAcceptTypes,表示可以打开的文件类型,我设置为*/*表示可以打开所有文件类型,可以根据实际需要改变它的值。2、读取待下载的文件的信息并分析在发送请求后,可以使用HttpQueryInfo函数获取文件的有关信息,或者取得服务器的信息以及服务器支持的相关操作。对于下载程序,最常用的是传递HTTP_QUERY_CONTENT_LENGTH参数取得文件的大小,即文件包含的字节数。模块如下所示:/ 取得待下载文件的大小int _fastcall THttpGetThread:GetWEBFileSize(void)tryDWORD BufLen=HTTPGET_BUFFER_MAX;DWORD dwIndex=0;bool RetQueryInfo=HttpQueryInfo(FhRequest,HTTP_QUERY_CONTENT_LENGTH,Buffer, &BufLen,&dwIndex);if( RetQueryInfo=false) throw(Exception(Error:HttpQueryInfo);DoOnStatusText(ok:HttpQueryInfo);int FileSize=StrToInt(Buffer); / 文件大小DoOnGetFileSize(FileSize);catch(Exception &exception)DoOnStatusText(exception.Message);return FileSize;模块中的DoOnGetFileSize是发出取得文件大小的事件。取得文件大小后,对于采用多线程的下载程序,可以按照这个值进行合适的文件分块,确定每个文件块的起点和大小。3、下载文件的模块开始下载前,还应该先安排好怎样保存下载结果。方法很多,我直接采用了C+ Builder提供的文件函数打开一个文件句柄。当然,也可以采用Windows本身的API,对于小文件,全部缓冲到内存中也可以考虑。/ 打开输出文件,以保存下载的数据DWORD THttpGetThread:OpenOutFile(void)tryif(FileExists(FOutFileName)DeleteFile(FOutFileName);iFileHandle=FileCreate(FOutFileName);if(iFileHandle=-1) throw(Exception(Error:FileCreate);DoOnStatusText(ok:CreateFile);catch(Exception &exception)DoOnStatusText(exception.Message);return 0;/ 执行下载过程void THttpGetThread:DoHttpGet(void)DWORD dwCount=OpenOutFile();try/ 发出开始下载事件DoOnStatusText(StartGet:InternetReadFile);/ 读取数据DWORD dwRequest; / 请求下载的字节数DWORD dwRead; / 实际读出的字节数dwRequest=HTTPGET_BUFFER_MAX;while(true)Application-ProcessMessages();bool ReadReturn = InternetReadFile(FhRequest,(LPVOID)Buffer,dwRequest,&dwRead);if(!ReadReturn)break;if(dwRead=0)break;/ 保存数据BufferdwRead=;FileWrite(iFileHandle, Buffer, dwRead);dwCount = dwCount + dwRead;/ 发出下载进程事件DoOnProgress(dwCount);Fsuccess=true;catch(Exception &exception)Fsuccess=false;DoOnStatusText(exception.Message);FileClose(iFileHandle); DoOnStatusText(End:InternetReadFile);下载过程并不复杂,与读取本地文件一样,执行一个简单的循环。当然,如此方便的编程还是得益于微软对网络协议的封装。4、释放占用的资源这个过程很简单,按照产生各个句柄的相反的顺序调用InternetCloseHandle函数即可。void THttpGetThread:EndHttpGet(void)if(FConnected)DoOnStatusText(Closing:InternetConnect);tryInternetCloseHandle(FhRequest);InternetCloseHandle(FhConnect);InternetCloseHandle(FhSession);catch(.)FhSession=NULL;FhConnect=NULL;FhRequest=NULL;FConnected=false;DoOnStatusText(Closed:InternetConnect);我觉得,在释放句柄后,把变量设置为NULL是一种良好的编程习惯。在这个示例中,还出于如果下载失败,重新进行下载时需要再次利用这些句柄变量的考虑。5、功能模块的调用这些模块的调用可以安排在线程对象的Execute方法中,如下所示:void _fastcall THttpGetThread:Execute()FrepeatCount=5;for(int i=0;i0)iFileHandle=FileOpen(FOutFileName,fmOpenWrite);FileSeek(iFileHandle,0,2); / 移动文件指针到末尾if(iFileHandle=-1) throw(Exception(Error:FileCreate);DoOnStatusText(ok:OpenFile);return dwCount;DeleteFile(FOutFileName);2、 在开始下载文件(即执行InternetReadFile函数)之前,先调整WEB上的文件指针。这就要求WEB服务器支持随机读取文件的操作,有些服务器对此作了限制,所以应该判断这种可能性。对DoHttpGet模块的修改如下,同样省略了相同的代码:void THttpGetThread:DoHttpGet(void)DWORD dwCount=OpenOutFile();if(dwCount0) / 调整文件指针dwStart = dwStart + dwCount;if(!SetFilePointer() / 服务器不支持操作/ 清除输出文件FileSeek(iFileHandle,0,0); / 移动文件指针到头部多线程下载要实现多线程下载,最主要的问题是下载线程的创建和管理,已经下载完成后文件的各个部分的准确合并,同时,下载线程也要作必要的修改。1、下载线程的修改为了适应多线程程序,我在下载线程加入如下成员变量:int FIndex; / 在线程数组中的索引DWORD dwStart; / 下载开始的位置DWORD dwTotal; / 需要下载的字节数DWORD FGetBytes; / 下载的总字节数并加入如下属性值:_property AnsiString URL = read=FURL, write=FURL ;_property AnsiString OutFileName = read=FOutFileName, write=FOutFileName;_property bool Successed = read=FSuccess;_property int Index = read=FIndex, write=FIndex;_property DWORD StartPostion = read=dwStart, write=dwStart;_property DWORD GetBytes = read=dwTotal, write=dwTotal;_property TOnHttpCompelete OnComplete = read=FOnComplete, write=FOnComplete ;同时,在下载过程DoHttpGet中增加如下处理,void THttpGetThread:DoHttpGet(void)trywhile(true)Application-ProcessMessages();/ 修正需要下载的字节数,使得dwRequest + dwCount 0) / dwTotal=0表示下载到文件结束if(dwRequest+dwCountdwTotal)dwRequest=dwTotal-dwCount;if(dwTotal0) / dwTotal =dwTotal)break;if(dwCount=dwTotal)FSuccess=true;2、建立多线程下载组件我先建立了以TComponent为基类、名为THttpGetEx的组件模块,并增加以下成员变量:/ 内部变量THttpGetThread *HttpThreads; / 保存建立的线程AnsiString *OutTmpFiles; / 保存结果文件各个部分的临时文件bool *FSuccesss; / 保存各个线程的下载结果/ 以下是属性变量int FHttpThreadCount; / 使用的线程个数AnsiString FURL;AnsiString FOutFileName;各个变量的用途都如代码注释,其中的FSuccess的作用比较特别,下文会再加以详细解释。因为线程的运行具有不可逆性,而组件可能会连续地下载不同的文件,所以下载线程只能动态创建,使用后随即销毁。创建线程的模块如下,其中GetSystemTemp函数取得系统的临时文件夹,OnThreadComplete是线程下载完成后的事件,其代码在其后介绍:/ 分配资源void THttpGetEx:AssignResource(void)FSuccesss=new boolFHttpThreadCount;for(int i=0;iFHttpThreadCount;i+)FSuccesssi=false;OutTmpFiles = new AnsiStringFHttpThreadCount;AnsiString ShortName=ExtractFileName(FOutFileName);AnsiString Path=GetSystemTemp();for(int i=0;iURL=FURL; / 初始化事件HttpThread-OnComplete=OnThreadComplete; / 线程下载完成事件return HttpThread;/ 创建下载线程数组void THttpGetEx:CreateHttpThreads(void)AssignResource();/ 取得文件大小,以决定各个线程下载的起始位置THttpGetThread *HttpThread=CreateHttpThread();HttpThreadsFHttpThreadCount-1=HttpThread;int FileSize=HttpThread-GetWEBFileSize();/ 把文件分成FHttpThreadCount块int AvgSize=FileSize/FHttpThreadCount;int *Starts= new intFHttpThreadCount;int *Bytes = new intFHttpThreadCount;for(int i=0;iStartPostion=StartsFHttpThreadCount-1;HttpThread-GetBytes=BytesFHttpThreadCount-1;bool CanMulti=HttpThread-SetFilePointer();if(CanMulti=false) / 不支持,直接下载FHttpThreadCount=1;HttpThread-StartPostion=0;HttpThread-GetBytes=FileSize;HttpThread-Index=0;HttpThread-OutFileName=OutTmpFiles0;elseHttpThread-OutFileName=OutTmpFilesFHttpThreadCount-1;HttpThread-Index=FHttpThreadCount-1;/ 支持断点续传,建立多个线程for(int i=0;iStartPostion=Startsi;HttpThread-GetBytes=Bytesi;HttpThread-OutFileName=OutTmpFilesi;HttpThread-Index=i;HttpThreadsi=HttpThread;/ 删除临时变量delete Starts;delete Bytes;下载文件的下载的函数如下:void _fastcall THttpGetEx:DownLoadFile(void)CreateHttpThreads();THttpGetThread *HttpThread;for(int i=0;iResume();线程下载完成后,会发出OnThreadComplete事件,在这个事件中判断是否所有下载线程都已经完成,如果是,则合并文件的各个部分。应该注意,这里有一个线程同步
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2024秋八年级地理上册 第2章 第一节《地形地势特征》说课稿1 (新版)商务星球版
- 2025服务类招标代理合同模板
- 2025年上海牌照转让规范合同书
- 教科版信息技术高一必修 4.1.2文字处理软件教学设计
- 大连事业单位笔试真题2025
- 7.2.2(一)平行线判定 说课稿-2024-2025学年人教版数学七年级下册
- Unit 1 My future教学设计-2023-2024学年小学英语五年级上册牛津沪教版(三起)
- 化肥厂应急演练组织规章
- 2024-2025学年高中语文 第二单元 六 我善养吾浩然之气说课稿5 新人教版选修《先秦诸子选读》
- 股权质押合同
- GB/T 45147-2024道路车辆总质量大于3.5 t的车辆气制动系统试验使用滚筒制动试验台获取和使用参考值
- 食管纵隔瘘护理
- 建筑项目水泥采购合同
- 华为ICT大赛网络赛道考试题库(786题)
- 水果采购协议样本
- 中职英语(高教版2021基础模块1)Part01-Unit2-Transportation
- 哲学与人生 第二课 树立科学的世界观2.1
- 2024-2030年中国止痛药品市场供需形势及未来前景动态研究研究报告
- 风电110KV升压站土建工程施工方案
- 2018低压电力线高速载波通信互联互通技术规范第3部分:检验方法
- 房屋漏水维修合同书范文
评论
0/150
提交评论