★卢培培★ ── 欢迎光临卢培培(goodname008)的BLOG

人生真正的快乐,多在贫家茅舍,少在富室红楼。

用户操作
[即时聊天] [发私信] [加为好友]
卢培培ID:goodname008
79303次访问,排名1255好友0人,关注者0
goodname008的文章
原创 22 篇
翻译 0 篇
转载 5 篇
评论 286 篇
卢培培的公告


只有干不成事的人
没有干不成的事


代码下载说明:请将代码下载地址的链接复制到浏览器的地址栏,按下回车即可正常下载。

最近评论
jingang123gz:在C# 中怎么屏蔽WIN键啊?????
kennylyj:我在做类似的东西的时候遇到一个问题:
由于需要将一些16位的DOS程序的输出导出,因此使用了管道,但直接CreateProcess这些程序是不行的,必须通过ComSpec这个环境变量得到命令行程序的路径并把要运行的DOS程序的路径及相关参数作为命令行程序的参数才行,或者是先运行cmd/command,然后通过标准写端口写入要启动的程序的参数
而这样做却导致系统的虚拟机程序……
mycaibo:原来可以下载呀,我还自己写了一遍,不过说KBDLLHOOKSTRUCT没有定义,也不知道是为什么,谢谢哈
jarcyju:大哥下载不了呀,麻烦发一份给我,谢谢啦
zhuyc0808@gmail.com
wuzhongyi:我认为以上的方案不好,看样子你也是做开发的。先不考虑硬盘本身的分区特点及如何得到更快的访问速度,

现在如果c:\为系统盘,而也是你常用的。那样XP安装完以后要多大?2G左右吧,那么虚拟的内存又会占用1G多,你再装点软件什么的。VS2005+msdn即使你不装在C盘,照样撑死你。
文章分类
收藏
    相册
    其它图片
    文章用图
    A.我的软件
    1.注册表大师 v2.0
    2.窗口间谍
    3.图易贴 v1.1
    B.强烈推荐
    1.VB打造超酷个性化菜单[一]
    2.VB打造超酷个性化菜单[二]
    3.VB打造超酷个性化菜单[三]
    4.剖析VC++函数调用约定
    C.有脚印的地方
    CSDN--中国软件网
    微软中国社区
    微软中文新闻组
    D.开放源代码
    1.鼠标感应器
    2.VB自绘菜单类
    3.你想要钱吗?
    E.友情链接
    1. 凝尘
    2. 羽毛羽毛
    3. 泗水寻芳
    存档
    软件项目交易
    订阅我的博客
    XML聚合  FeedSky
    订阅到鲜果
    订阅到Google
    订阅到抓虾
    订阅到BlogLines
    订阅到Yahoo
    订阅到GouGou
    订阅到飞鸽
    订阅到Rojo
    订阅到newsgator
    订阅到netvibes

    原创 利用HTTP协议实现文件下载的多线程断点续传收藏

     | 旧一篇: 将CMD的输入输出重定向到自己的进程

         最近研究了一下关于文件下载的相关内容,觉得还是写些东西记下来比较好。起初只是想研究研究,但后来发现写个可重用性比较高的模块还是很有必要的,我想这也是大多数开发人员的习惯吧。

         对于HTTP协议,向服务器请求某个文件时,只要发送类似如下的请求即可:

     

         GET /Path/FileName HTTP/1.0

    Host: www.server.com:80

    Accept: */*

    User-Agent: GeneralDownloadApplication

    Connection: close

     

         每行用一个“回车换行”分隔,末尾再追加一个“回车换行”作为整个请求的结束。

    第一行中的GETHTTP协议支持的方法之一,方法名是大小写敏感的,HTTP协议还支持OPTIONSHAEDPOSTPUTDELETETRACECONNECT等方法,而GETHEAD这两个方法通常被认为是“安全的”,也就是说任何实现了HTTP协议的服务器程序都会实现这两个方法。对于文件下载功能,GET足矣。GET后面是一个空格,其后紧跟的是要下载的文件从WEB服务器根开始的绝对路径。该路径后又有一个空格,然后是协议名称及协议版本。

    除第一行以外,其余行都是HTTP头的字段部分。Host字段表示主机名和端口号,如果端口号是默认的80则可以不写。Accept字段中的*/*表示接收任何类型的数据。User-Agent表示用户代理,这个字段可有可无,但强烈建议加上,因为它是服务器统计、追踪以及识别客户端的依据。Connection字段中的close表示使用非持久连接。

    关于HTTP协议更多的细节可以参考RFC2616HTTP 1.1)。因为我只是想通过HTTP协议实现文件下载,所以也只看了一部分,并没有看全。

    如果服务器成功收到该请求,并且没有出现任何错误,则会返回类似下面的数据:

     

    HTTP/1.0 200 OK

    Content-Length: 13057672

    Content-Type: application/octet-stream

    Last-Modified: Wed, 10 Oct 2005 00:56:34 GMT

    Accept-Ranges: bytes

    ETag: "2f38a6cac7cec51:160c"

    Server: Microsoft-IIS/6.0

    X-Powered-By: ASP.NET

    Date: Wed, 16 Nov 2005 01:57:54 GMT

    Connection: close

     

    不用逐一解释,很多东西一看几乎就明白了,只说我们大家都关心内容吧。

    第一行是协议名称及版本号,空格后面会有一个三位数的数字,是HTTP协议的响应状态码,200表示成功,OK是对状态码的简短文字描述。状态码共有5类:1xx属于通知类;2xx属于成功类;3xx属于重定向类;4xx属于客户端错误类;5xx属于服务端错误类。对于状态码,相信大家对404应该很熟悉,如果向一个服务器请求一个不存在的文件,就会得到该错误,通常浏览器也会显示类似“HTTP 404 - 未找到文件”这样的错误。Content-Length字段是一个比较重要的字段,它标明了服务器返回数据的长度,这个长度是不包含HTTP头长度的。换句话说,我们的请求中并没有Range字段(后面会说到),表示我们请求的是整个文件,所以Content-Length就是整个文件的大小。其余各字段是一些关于文件和服务器的属性信息。

    这段返回数据同样是以最后一行的结束标志(回车换行)和一个额外的回车换行作为结束,即“\r\n\r\n”。而“\r\n\r\n”后面紧接的就是文件的内容了,这样我们就可以找到“\r\n\r\n”,并从它后面的第一个字节开始,源源不断的读取,再写到文件中了。

    以上就是通过HTTP协议实现文件下载的全过程。但还不能实现断点续传,而实际上断点续传的实现非常简单,只要在请求中加一个Range字段就可以了。

    假如一个文件有1000个字节,那么其范围就是0-999,则:

     

    Range: bytes=500-      表示读取该文件的500-999字节,共500字节。

         Range: bytes=500-599   表示读取该文件的500-599字节,共100字节。

     

         Range还有其它几种写法,但上面这两种是最常用的,对于断点续传也足矣了。如果HTTP请求中包含Range字段,那么服务器会返回206Partial Content),同时HTTP头中也会有一个相应的Content-Range字段,类似下面的格式:

     

         Content-Range: bytes 500-999/1000

     

    Content-Range字段说明服务器返回了文件的某个范围及文件的总长度。这时Content-Length字段就不是整个文件的大小了,而是对应文件这个范围的字节数,这一点一定要注意。

    一切好像基本上没有什么问题了,本来我也是这么认为的,但事实并非如此。如果我们请求的文件的URL是类似http://www.server.com/filename.exe这样的文件,则不会有问题。但是很多软件下载网站的文件下载链接都是通过程序重定向的,比如pchomeACDSeeHTTP下载地址是:

    http://download.pchome.net/php/tdownload2.php?sid=5547&url=/multimedia/viewer/acdc31sr1b051007.exe&svr=1&typ=0

    这种地址并没有直接标识文件的位置,而是通过程序进行了重定向。如果向服务器请求这样的URL,服务器就会返回302Moved Temporarily),意思就是需要重定向,同时在HTTP头中会包含一个Location字段,Location字段的值就是重定向后的目的URL。这时就需要断开当前的连接,而向这个重定向后的服务器发请求。

         好了,原理基本上就是这些了。其实装个Sniffer好好分析一下,很容易就可以分析出来的。不过NetAnts也帮了我一些忙,它的文件下载日志对开发人员还是很有帮助的。

    我在写这段程序时,一开始也仅仅是实现了文件下载的功能,后来又考虑到回调、HTTP重定向、多线程断点续传、统计BPS、允许获得HTML错误页等功能,代码越写越长,不过还好,不是很恐怖,但要全部贴出来也不合适,所以只拣了些关键的。

         接口是这样设计的:

     

     

    #ifndef __HTTP_DOWNLOAD_FILE_H__

    #define __HTTP_DOWNLOAD_FILE_H__

     

    #ifdef HTTPDOWNLOADFILE_EXPORTS

    #define HTTPDOWNLOADFILE_API __declspec(dllexport)

    #else

    #define HTTPDOWNLOADFILE_API /*__declspec(dllimport)*/

    #endif

     

    #ifdef __cplusplus

    extern "C" {

    #endif

     

    // HTTP Download File Error Codes

    #define HTTPDF_OK                                  0x00000000

    #define HTTPDF_ERROR_SOCKET                        0x00000001

    #define HTTPDF_ERROR_URL                           0x00000002

    #define HTTPDF_ERROR_CONNECT                       0x00000003

    #define HTTPDF_ERROR_REQUEST                       0x00000004

    #define HTTPDF_ERROR_FILE_IO                       0x00000005

    #define HTTPDF_ERROR_TIMEOUT                       0x00000006

    #define HTTPDF_ERROR_HTTP                          0x00000007

    #define HTTPDF_ERROR_BUFFER_SIZE                    0x00000008

    #define HTTPDF_ERROR_USER_CANCELED                 0x00000009

    #define HTTPDF_ERROR_INVALID_PARAMETER             0x0000000A

     

    // HTTP Download File Flags

    #define HTTPDF_FLAG_HTTP_ERROR_PAGE                0x00000001

    #define HTTPDF_FLAG_CALLBACK                       0x00000002

    #define HTTPDF_FLAG_GET_FILE_SIZE_ONLY             0x00000004

    #define HTTPDF_FLAG_DELETE_INVALID_FILE            0x00000008

    #define HTTPDF_FLAG_RESUME_POSITION                0x00000010

    #define HTTPDF_FLAG_END_POSITION                    0x00000020

    #define HTTPDF_FLAG_TIMEOUT                        0x00000040

     

    // HTTP Download File Status Codes

    #define HTTPDF_STATUS_CONNECTING               0x00000001

    #define HTTPDF_STATUS_DOWNLOADING                  0x00000002

     

    // HTTP Response Status Codes (from wininet.h)

    #define HTTP_STATUS_CONTINUE            100 // OK to continue with request

    #define HTTP_STATUS_SWITCH_PROTOCOLS    101 // server has switched protocols in upgrade header

     

    #define HTTP_STATUS_OK                  200 // request completed

    #define HTTP_STATUS_CREATED             201 // object created, reason = new URI

    #define HTTP_STATUS_ACCEPTED            202 // async completion (TBS)

    #define HTTP_STATUS_PARTIAL             203 // partial completion

    #define HTTP_STATUS_NO_CONTENT          204 // no info to return

    #define HTTP_STATUS_RESET_CONTENT       205 // request completed, but clear form

    #define HTTP_STATUS_PARTIAL_CONTENT     206 // partial GET furfilled

     

    #define HTTP_STATUS_AMBIGUOUS           300 // server couldn't decide what to return

    #define HTTP_STATUS_MOVED               301 // object permanently moved

    #define HTTP_STATUS_REDIRECT            302 // object temporarily moved

    #define HTTP_STATUS_REDIRECT_METHOD     303 // redirection w/ new access method

    #define HTTP_STATUS_NOT_MODIFIED        304 // if-modified-since was not modified

    #define HTTP_STATUS_USE_PROXY           305 // redirection to proxy, location header specifies proxy to use

    #define HTTP_STATUS_REDIRECT_KEEP_VERB  307 // HTTP/1.1: keep same verb

     

    #define HTTP_STATUS_BAD_REQUEST         400 // invalid syntax

    #define HTTP_STATUS_DENIED              401 // access denied

    #define HTTP_STATUS_PAYMENT_REQ         402 // payment required

    #define HTTP_STATUS_FORBIDDEN           403 // request forbidden

    #define HTTP_STATUS_NOT_FOUND           404 // object not found

    #define HTTP_STATUS_BAD_METHOD          405 // method is not allowed

    #define HTTP_STATUS_NONE_ACCEPTABLE     406 // no response acceptable to client found

    #define HTTP_STATUS_PROXY_AUTH_REQ      407 // proxy authentication required

    #define HTTP_STATUS_REQUEST_TIMEOUT     408 // server timed out waiting for request

    #define HTTP_STATUS_CONFLICT            409 // user should resubmit with more info

    #define HTTP_STATUS_GONE                410 // the resource is no longer available

    #define HTTP_STATUS_LENGTH_REQUIRED     411 // the server refused to accept request w/o a length

    #define HTTP_STATUS_PRECOND_FAILED      412 // precondition given in request failed

    #define HTTP_STATUS_REQUEST_TOO_LARGE   413 // request entity was too large

    #define HTTP_STATUS_URI_TOO_LONG        414 // request URI too long

    #define HTTP_STATUS_UNSUPPORTED_MEDIA   415 // unsupported media type

    #define HTTP_STATUS_RETRY_WITH          449 // retry after doing the appropriate action.

     

    #define HTTP_STATUS_SERVER_ERROR        500 // internal server error

    #define HTTP_STATUS_NOT_SUPPORTED       501 // required not supported

    #define HTTP_STATUS_BAD_GATEWAY         502 // error response received from gateway

    #define HTTP_STATUS_SERVICE_UNAVAIL     503 // temporarily overloaded

    #define HTTP_STATUS_GATEWAY_TIMEOUT     504 // timed out waiting for gateway

    #define HTTP_STATUS_VERSION_NOT_SUP     505 // HTTP version not supported

     

    typedef struct tagDOWNLOADFILEPROGRESS

    {

         DWORD         dwStatus;

         DWORD         dwHTTPStatusCode;

         LPCTSTR       lpszHostName;

         LPCTSTR       lpszFileRealURL;

         WORD          nPort;

         LPCTSTR       lpszFileName;

         DWORD         dwFileSize;

         DWORD         dwBytesWritten;

         DWORD         dwContentLength;

         DWORD         dwResumePos;

         DWORD         dwTimeElapsed;

         LPVOID        lpParameter;

     

    } DOWNLOADFILEPROGRESS, *LPDOWNLOADFILEPROGRESS;

     

    typedef BOOL (CALLBACK* LPFNHTTPDOWNLOADFILE)(LPDOWNLOADFILEPROGRESS lpProgress);

     

    typedef struct tagHTTPDOWNLOADFILEINFO

    {

         DWORD         cbSize;

         DWORD         dwFlags;

         LPCTSTR       lpszFileURL;

         LPTSTR        lpszFileSavePath;

         DWORD         dwPathLen;

         LPCTSTR       lpszFileErrorPage;

         DWORD         dwResumePos;

         DWORD         dwEndPos;

         DWORD         dwTimeOut;

         DWORD         dwHTTPStatusCode;

         DWORD         dwFileSize;

         DWORD         dwBytesWritten;

         DWORD         dwError;

         LPVOID        lpParameter;

         LPVOID        lpReserved;

         LPFNHTTPDOWNLOADFILE pfnCallback;

        

    } HTTPDOWNLOADFILEINFO, *LPHTTPDOWNLOADFILEINFO;

     

    HTTPDOWNLOADFILE_API BOOL HTTPDownloadFile(LPHTTPDOWNLOADFILEINFO lpDownloadFileInfo);

     

    #ifdef __cplusplus

    }

    #endif

     

    #endif /* __HTTP_DOWNLOAD_FILE_H__ */

     

         DLL只有一个导出函数,即HTTPDownloadFile,该函数需要一个HTTPDOWNLOADFILEINFO结构的参数,结构的各个成员如下:

     

    cbSize

    该结构的大小,以字节为单位。

    必须正确设置该成员,否则会返回HTTPDF_ERROR_INVALID_PARAMETER错误。

     

    dwFlags

             标志位的组合,可以是如下各标志的逻辑加(|):

        

    含义

    HTTPDF_FLAG_HTTP_ERROR_PAGE

    接收HTTP错误页,如果指定该选项,则lpszFileErrorPage为保存错误页的路径及文件名。在下载文件过程中如果发生HTTP错误,则将服务器返回的HTTP错误页的内容写入到该文件中。

    HTTPDF_FLAG_CALLBACK

    指明pfnCallback成员有效,此时pfnCallback是指向回调函数的指针,不能为空,否则会返回HTTPDF_ERROR_INVALID_PARAMETER错误。

    HTTPDF_FLAG_GET_FILE_SIZE_ONLY

    只获得文件大小,而不下载文件。