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