《C++编程艺术》第五章 下载工具源码

今天看了书上的第五章代码,看了后想编译起来结果报了好些错,修改完后已经可以正确的编译起来,供大家下载研究

// Header file for downloader.  Call this file dl.h.
#include <iostream>
#include <string>
#include <windows.h>
#include <wininet.h>
#include <fstream> 

using namespace std; 

const int MAX_ERRMSG_SIZE = 80; 

// 用于下载工具抛出异常的类.
class DLExc {
    char err[MAX_ERRMSG_SIZE];
public:
    DLExc(char *exc) {
        if(strlen(exc) < MAX_ERRMSG_SIZE)
            strcpy_s(err, exc);
    }  

    // Return a pointer to the error message.
    const char * geterr() {
        return err;
    }
};  

// Download 类处理文件的下载,这个只包含静态函数
// 这个类只支持http1.1以及更高版本的URL下载,因为
// 它需要ranger报头,提供断点下载功能

class Download {
    static bool ishttp(char *url);
    static bool httpverOK(HINTERNET hIurl);
    static bool getfname(char *url, char *fname);
    static unsigned long openfile(char *url, bool reload,
        ofstream &fout);
public:
    static bool dowld(char *url, bool restart=false,
        void (*update)(unsigned long, unsigned long)=NULL);
};  

dl.h头文件代码

// A file dowld subsystem.
#include "afx.h"
#include <windows.h>
#include <wininet.h>
#include <iostream>
#include <fstream>
#include "dl.h"

#pragma comment(lib, "Wininet.lib")

using namespace std;

#ifndef MAX_ERRMSG_SIZE
#define MAX_ERRMSG_SIZE 80
#endif // !1

#ifndef MAX_FILENAME_SIZE
#define MAX_FILENAME_SIZE 512
#endif

//#ifndef BUFFERSIZE
//#define BUFFERSIZE 1024;
//#endif
    //const int MAX_FILENAME_SIZE = 512;
    const int BUFFERSIZE = 1024;
    //const int MAX_ERRMSG_SIZE = 80;
// 用于下载工具抛出异常的类.
class DowdLoadExc {
    char err[MAX_ERRMSG_SIZE];
public:
    DowdLoadExc(char *exc) {
        if(strlen(exc) < MAX_ERRMSG_SIZE)
            strcpy_s(err, exc);
    }  

    // Return a pointer to the error message.
    const char * geterr() {
        return err;
    }
};  

// dowld 类处理文件的下载,这个只包含静态函数
// 这个类只支持http1.1以及更高版本的URL下载,因为
// 它需要ranger报头,提供断点下载功能
//class Download {
//    bool ishttp(char *url);
//    bool httpverOK(HINTERNET hIurl);
//    bool getfname(char *url, char *fname);
//    unsigned long openfile(char *url, bool reload,
//        ofstream &fout);
//public:
//    bool dowld(char *url, bool restart=false,
//        void (*update)(unsigned long, unsigned long)=NULL);
//};  

//
// dowld函数传进一个URL,,如果文件已经在磁盘则继续下载
// 否则新建下载
// reload参数,指示是否再次下载某个已下载过的文件
// update函数指针作为了解下载过程的一种方法,被dowld函数
// 反复调用
bool Download::dowld(char *url, bool reload,
                        void (*update)(unsigned long, unsigned long))
{  

    ofstream fout;                       // 输出流
    char buf[BUFFERSIZE];                 // 输入缓冲区
    unsigned long numrcved;              // 每次循环已收到字节数
    unsigned long filelen;               // 文件占用空间大小
    HINTERNET hIurl = NULL , hInet = NULL;              // hUrl为URL句柄,hInet为连接Internet的句柄
    unsigned long contentlen;            // 内容的数量
    unsigned long len;                   // length of contentlen
    unsigned long total = 0;             // 总已下载的字节数
    char header[80];                     // 保存range报头 

    try {
        //判断是否是http连接
        if(!ishttp(url))
            throw DowdLoadExc("Must be HTTP url.");  

        // 通过指定的URL打开文件.
        // 如果文件不存在,或要重新下载,则opfile返回
        // 文件大小为0,否则返回已存在文件的大小
        filelen = openfile(url, reload, fout);  

        // 文件打开后,建立起以Internet的连,这个函数只有一个参数
        // 且这个参数必须为0.
        if(InternetAttemptConnect(0) != ERROR_SUCCESS)
            throw DowdLoadExc("Can‘t connect.");  

        // 如果链接可用,就会调用InternetOpen打开这个链接
        // 第一个参数指定程序名,第二个指定使用的访问类型
        // 指定当前这个类型是,第三第四必须为NULL,最后一个指定任意选项
        hInet = InternetOpen("downloader",
            INTERNET_OPEN_TYPE_DIRECT,
            NULL, NULL,  0);  

        if(hInet == NULL)
            throw DowdLoadExc("Can‘t open connection.");  

        //一旦打开链接就会生成下面的range报头
        sprintf_s(header, "Range:bytes=%d-", filelen);   

        // 将range报头传给下面的函数,打开指定的URL
        hIurl = InternetOpenUrl(hInet, url,
            header, -1,
            INTERNET_FLAG_NO_CACHE_WRITE, 0);  

        if(hIurl == NULL) throw DowdLoadExc("Can‘t open url.");  

        //  检查HTTP的版本是否是1.1或者是更高的版本.
        if(!httpverOK(hIurl))
            throw DowdLoadExc("HTTP/1.1 not supported.");  

        // 获取下载内容的数量
        // 内容的长度存储在contentlen,内容长度自动考虑range报头的
        // 请求范围,如果是整个下载,则从0到-(整个文件长度),断点续传
        // 的话,将返回中的一个节点,内容长度的为剩余长度
        // HTTP_QUERY_CONTENT_LENGTH用来获取被请求内容长度, HTTP_QUERY_FLAG_NUMBER
        // 要求这个值以整形值表示
        len = sizeof contentlen;
        if(!HttpQueryInfo(hIurl,
            HTTP_QUERY_CONTENT_LENGTH |
            HTTP_QUERY_FLAG_NUMBER,
            &contentlen, &len, NULL))
            throw DowdLoadExc("File or content length not found.");  

        // 如果已存在文件未完成,则继续下载
        if(filelen != contentlen && contentlen)
            do {
                // 调用InternetReadFile来读取文件每次读取一个缓冲区就会循环一次
                // 每循环一次都会调用update指向的函数,除非update为NULL
                // 最后一个参数为实际读取成功的字节数
                // 如果成功这个函数返回true
                if(!InternetReadFile(hIurl, &buf,
                    BUFFERSIZE, &numrcved))
                    throw DowdLoadExc("Error occurred during dowld.");  

                // 将缓存写入到文件中
                fout.write((const char *) buf, numrcved);
                if(!fout.good())
                    throw DowdLoadExc("Error writing file.");  

                total += numrcved;                                                             // 更新已下载的字节总数  

                // 如果已定义update函数,则调用
                if(update && numrcved > 0)
                    update(contentlen+filelen, total+filelen);  

            } while(numrcved > 0);                                                          //当每次接收的字节大于0则继续,为0一般就是接收完成
        else
            if(update)
                update(filelen, filelen);  

    } catch(DowdLoadExc) {
        fout.close();
        InternetCloseHandle(hIurl);
        InternetCloseHandle(hInet);  

        throw; // rethrow the exception for use by caller
    }  

    fout.close();
    InternetCloseHandle(hIurl);
    InternetCloseHandle(hInet);  

    return true;
}  

// 检查是否是http1.1版本
bool Download::httpverOK(HINTERNET hIurl) {
    char str[80];
    unsigned long len = 79;  

    // 获取http版本,如果是1.1,str内容为HTTP/1.1.
    if(!HttpQueryInfo(hIurl, HTTP_QUERY_VERSION, &str, &len, NULL))
        return false;  

    // First, check major version number.
    char *p = strchr(str, ‘/‘);
    p++;
    if(*p == ‘0‘) return false; // 不支持 HTTP 0.x 

    // Now, find start of minor HTTP version number.
    p = strchr(str, ‘.‘);
    p++;  

    // Convert to int.
    int minorVerNum = atoi(p);  

    if(minorVerNum > 0) return true;
    return false;
}  

// 从URL中获取文件名.
bool Download::getfname(char *url, char *fname) {
    // Find last /.
    char *p = strrchr(url, ‘/‘);  

    // Copy filename after the last /.
    size_t len = strlen(p);
    if(p && ( len < MAX_FILENAME_SIZE)) {
        //p++;
        strcpy_s(fname,len,p + 1);
        return true;
    }
    else
        return false;
}  

// 打开文件,初始化输出流,返回文件的大小
unsigned long Download::openfile(char *url,
                                 bool reload,
                                 ofstream &fout)
{
    char fname[MAX_FILENAME_SIZE];  

    if(!getfname(url, fname))
        throw DowdLoadExc("File name error.");  

    if(!reload)
        fout.open(fname, ios::binary | ios::out |
        ios::app | ios::ate);
    else
        fout.open(fname, ios::binary | ios::out |
        ios::trunc);    

    if(!fout)
        throw DowdLoadExc("Can‘t open output file.");    

    // 返回当前文件的长度.
    return fout.tellp();
}  

// 检查这个URL是否是HTTP链接,通过前四个字母检查.
bool Download::ishttp(char *url) {
    char str[5] = "";  

    // Get first four characters from URL.
    strncpy_s(str, url, 4);  

    // Convert to lowercase
    for(char *p=str; *p; p++) *p = tolower(*p);  

    return !strcmp("http", str);
}

dl.cpp代码

#include <iostream>
#include "dl.h" 

// This function displays the download progress as a percentage.
void showprogress(unsigned long total, unsigned long part) {
    int val = (int) ((double) part/total*100);
    cout << val << "%" << endl;
} 

int main(int argc, char *argv[])
{ 

    // This URL is for demonstration purposes only.  Substitute
    // the URL of the file that you want to download.
    char url[] =
        "http://zip.77nt.com/18/%E5%9B%BD%E5%AE%B6%E6%84%8F%E5%BF%9777nt.com_txt18685.txt"; 

    bool reload = false; 

    if(argc==2 && !strcmp(argv[1], "reload"))
        reload = true; 

    cout << "Beginning download.\n"; 

    try {
        if(Download::dowld(url, reload, showprogress))
            cout << "Download Complete\n";
    } catch(DLExc exc) {
        cout << exc.geterr() << endl;
        cout << "Download Interrupted\n";
    }    

    system("pause");
    return 0;
}

测试用的main函数

时间: 2024-10-08 20:52:03

《C++编程艺术》第五章 下载工具源码的相关文章

openwrt教程 第二章 下载openwrt源码

2.1 开发环境 我们工作室(F403科技创意室:http://f403tech.taobao.com/)写的该教程,所使用的环境为: VMware Workstation:VMware 8 Ubuntu:Ubuntu12.04 具体环境搭建过程,可以向客服索要用户手册!上面有非常详细的过程! 2.2 准备工作 再下载.配置.编译openwrt系统之前,我们需要做些准备工作,安装一些必须的工具.库. (1) 安装SVN工具 安装SVN工具,用于下载openwrt源码: $ sudo apt-ge

《Python核心编程》 第五章 数字 - 课后习题

课后习题  5-1 整形. 讲讲 Python 普通整型和长整型的区别. 答:普通整型是绝大多数现代系统都能识别的. Python的长整型类型能表达的数值仅仅与你机器支持的(虚拟)内存大小有关. 5-2 运算符 (a) 写一个函数,计算并返回两个数的乘积 (b) 写一段代码调用这个函数,并显示它的结果 答: def pro(a,b): p = a*b return p a = int(raw_input("a=")) b = int(raw_input("b="))

JavaScript Dom编程艺术 第6章的一个错误

今天在看JavaScript Dom编程艺术 第6章:图片库的改进版时:按照书上的代码,敲出来运行,确怎么也不能显示出正确的结果.加进去断点,调试,发现:prepareGallery 函数根本没被调用,而prepareGallery函数是绑定到window.onLoad事件上的,于是仔细检查书上代码,发现,window.onLoad = prepareGallery;后边少加了个括号.加上括号之后,结果正确.改正后的代码如下: 1 function addLoadEvent(func){ 2 v

CCNA学习指南 第五章 下载

在一个陌生的环境里如果想到达某一个地点,可以有三种方法:第一种方法请一位向导,只要把你要去的目的地说清楚了,一路上不用操心,向导自然会带你到达.第二种方法找一张相关的地图,按图索骥.第三种方法是朝着目的地的方向边走边问,根据被询问的人提供的信息选择一条最近的路径. CCNA学习指南 第五章 下载

跟我学SpringMVC目录汇总贴、PDF下载、源码下载

跟我学SpringMVC目录汇总贴.PDF下载.源码下载 http://jinnianshilongnian.iteye.com/blog/1752171 跟开涛学SpringMVC 在线版目录 第一章 Web MVC简介 第二章 Spring MVC入门 第三章 DispatcherServlet详解 第四章 Controller接口控制器详解(1) 第四章 Controller接口控制器详解(2) 第四章 Controller接口控制器详解(3) 第四章 Controller接口控制器详解(

专业定制下载系统网站/下载网站源码,资源下载网站源码

10年的技术团队专业定制下载系统网站/下载网站源码,资源下载网站平台定制 该下载系统由绿茶科技团队自主开发,系统采用了国内比较主流的thinkPHP框架实现的,数据库用MySQL.是一套致力于软件应用,工具游戏,视频小说,教程资讯等通用资源下载的下载系统整站源码.模版设计整洁.清爽,广告位布局合理.会员积分体系完善,支持用户上传分享免费.收费资源. 下载收益支持用户提现. 分享有收益,刺激用户上传扩充网站资源,实现商家与平台联和共盈. PC版: 手机版: 服务器选择:  服务器购买地址:http

第66讲:Scala并发编程实战初体验及其在Spark源码中的应用解析

王家林亲授<DT大数据梦工厂>大数据实战视频“Scala深入浅出实战经典”视频.音频和PPT下载!第66讲:Scala并发编程实战初体验及其在Spark源码中的应用解析百度云:http://pan.baidu.com/s/1pJ5jzHx腾讯微云:http://url.cn/aSawrm360云盘:http://yunpan.cn/cctL3QYACaVNa  访问密码 c0fb 信息来源于 DT大数据梦工厂微信公众账号:DT_Spark

unbuntu 下载android源码

在Windows下安装Cygwin,通过Cygwin也可在Windows里通过本文的下载步骤下载Android源码. 以下为在Ubuntu下下载Google Android4.4源码的步骤: 1. 安装curl 与 git sudo apt-get install curl sudo apt-get install git-core 2 安装 Repo a) 建立Repo的安装目录.配置环境变量 $ mkdir ~/bin $ PATH=~/bin:$PATH  b) 获取Repo工具 $ cu

下载Android源码(Ubuntu)

 下载Android源码(Unbuntu环境下) 1.sudo apt-get install git-core curl   #安装这两个工具 2.mkdir -p /develop/download-froyo    #根目录下建立响应工作目录 3.cd ~/develop/download-froyo 4.curl http://Android.git.kernel.org/repo > ./repo   #下载repo脚本 #现在一般会出现curl: (6) Couldn't res