《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函数

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。