代码改变世界

学了C语言,如何利用CURL写一个下载程序?—用nmake编译CURL并安装

2013-11-01 11:39  kingshow  阅读(1580)  评论(0编辑  收藏  举报

在这一系列的前一篇文章学了C语言,如何为下载狂人写一个磁盘剩余容量监控程序?中,我们为下载狂人写了一个程序来监视磁盘的剩余容量,防止下载的东西撑爆了硬盘。可是,这两天,他又抱怨他的下载程序不好用,让我们帮他写一个下载程序。

虽然我们学了C语言,虽说学了C语言,走遍天下都不怕,但是要想用C语言写一个全新的下载程序,对我们来说,就好比另外一个二万五千里长征。虽然技术上可行,但是在实际操作中却是行不通的。谁也不会傻到去用C语言写一个全新的下载程序。那么,到底怎么办呢?

如果我们看了《C程序设计伴侣》的7.A 站在巨人的肩膀上——如何使用已有的函数库这个节,要想解决这个问题,那简直就是“洒洒水啦”。在这个小节中,我们介绍了如何借助一些已有的函数库来帮助我们高质量高效率地完成一些常见任务。而其中介绍的CURL,正好是一个可以用于网络下载的开源函数库。

实际上,CURL有两种工作方式,其一,它是一个用URL语法在命令行方式下工作的文件传输工具。我们可以使用curl.exe直接在命令行下下载文件。其二,它也是一个开源的函数库,它提供了libcurl这个函数库,其中提供了多个可以用于处理网络事务(诸如下载,登陆,POST等)的API,利用这些API,我们就可以轻松地完成一个下载程序,这就好像坐上了飞机,片刻之间,就从江西瑞金到了陕北延安,一步完成二万五千里长征。

CURL的使用非常简单,跟其他类似的开源的函数库一样,其使用分为编译函数库和使用函数库两个步骤。

编译CURL函数库

在这里,我们以在Windows环境下,利用Visual C++ 2012编译为例。如果是其他环境,可以参考其文档,也很详尽。

    1. 从CURL的官方网站上下载CURL的源代码包,并解压得到所有源代码
    2. 接下来的工作就是源代码编译成相应的函数库文件(lib和dll)了。这里有两种方法:
    3. 我们可以采用《C程序设计伴侣》中介绍的方法,用Visual Studio打开根目录下的vc6curl文件,经过项目转换后,使用Visual C++ 2012的IDE编译其中的libcurl项目。
    4. 更简便的,我们还可以在“VS2012开发人员命令行提示”中,使用nmake来编译整个源代码。
    5. 首先是启动位于开始->所有程序->Microsoft Visual Studio 2012->Visual Studio Tools中的命令提示,然后用cd命令进入源代码所在根目录下的winbuild目录,比如:     C:\Program Files\Microsoft Visual Studio 11.0>f:

F:\>cd code\curl-7.28.1d\winbuild

F:\code\curl-7.28.1d\winbuild>

  1. 然后,就是使用nmake编译整个代码了:     nmake /f Makefile.vc mode=dll vc=10      其中,mode=dll表示编译成动态链接库,而vc=10选择的是Visual C++的版本,本来Visual C++ 2012是vc11,但是CURL不支持,只好用2010的10代替了,编译也没有什么问题。
  2. 起来倒杯水的时间,整个编译就完成了,编译结果保存在     F:\code\curl-7.28.1d\builds\libcurl-vc10-x86-release-dll-ipv6-sspi-spnego-winssl/      目录下,其中bin保存了可以用于命令行的curl.exe应用程序和它用到的libcurl.dll,include目录是开发需要用到的头文件,lib目录下是需要用到的库文件。
  3. 最后的工作,就是安装libcurl函数库。     将/builds/lib/目录下的文件复制到编译器的库文件目录,例如,E:\MinGW\lib\      将/builds/include/目录下的文件复制到编译器的头文件目录,例如,E:\MinGW\include\      将/builds/bin/目录下的libcurl.dll文件复制到操作系统的动态库搜索目录,例如,C:\Windows\System32\      或者可执行文件所在的目录      当然,我们可以通过配置开发工具来达到同样的目的,这里就不赘述了。
  4. 如果你嫌自己编译麻烦,CURL也提供已经编译好的二进制文件可以直接下载,不过我还是建议你自己动手编译,毕竟,这是我们每个程序员都需要掌握的基本功。

 

至此,整个编译安装过程就算完成了,这样我们就可以在我们自己的程序中调用CURL所提供的API来完成文件下载的功能。这就像飞机已经加满了油,正停在跑道上等待起飞!

塔台塔台,CL2013次航班准备完毕,请求起飞。

现在,我们就可以利用其中提供的API来简便地实现一个系在程序。
/*
 *  get.c
 *  简易下载程序
 *  Created on: 2013年11月1日11:13:10
 *  Author: Bruce
 */

#include <curl/curl.h>
#include <string.h>
//用#pragam comment预编译命令,表示这个程序将使用libcurld_imp.lib这个文件
#pragma comment(lib,"libcurl.lib")

//复制将数据写入文件的回调函数,关于回调函数,可以参考C程序设计伴侣8.5.4小节介绍
size_t write_data(void * ptr,size_t size,size_t nmemb,FILE * stream)
{
    int written  = fwrite(ptr,size,nmemb,stream);
    return written;
}

//使用函数库所提供函数实现文件下载函数
CURLcode download(char * url,char * out)  //char * url待下载文件的URL,char * out 下载后的文件名
{
    CURL * curl = NULL;
    FILE * fp = NULL;
    CURLcode res;
    //调用函数库中的curl_easy_init()函数完成初始化
    curl = curl_easy_init();
    if(curl)  //判断初始化是否成功
    {
        //以二进制可写方式打开文件,保存下载得到的数据
        fp = fopen(out,"wb");
        //根据libcurl的使用方法,设定下载的URL,写入函数以及写入的文件
        curl_easy_setopt(curl,CURLOPT_URL,url);
        curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,write_data);
        curl_easy_setopt(curl,CURLOPT_WRITEDATA,fp);
        //调用curl_easy_perform()函数执行下载操作
        res = curl_easy_perform(curl);
        //下载完成,进行最后的清理工作
        curl_easy_cleanup(curl);
        //关闭文件
        fclose(fp);
        return res;
    }
    else
    {
        //如果初始化失败,返回相应的错误代码
        return CURL_FAILED_INIT;
    }
}

//辅助函数,用于从URL中获得文件名
void getfilename(char * url,char * name)
{
    //找到URL中的最后一个‘/’字符
    char* pos = strrchr(url,'/');
    if(NULL != pos)
    {
        //将URL中文件名(从pos+1开始知道字符串结束)复制到name字符串中
        strcpy(name,pos+1);
    }
}

//带有参数的主函数,可以让用户在执行时,直接输入待下载文件的URL
// 例如,如果我们想要下载libcurl的源代码包,则可以直接输入:
// get http://curl.haxx.se/download/curl-7.24.0.zip
// 关于带参数的主函数,可以参考8.7.3小节
int main(int argc,char* argv[])
{
    //获取用户输入的URL,判断使用方法是否正确
    if(2 != argc)
    {
         // 提示正确的使用方法
        puts("usage: get http://curl.haxx.se/download/curl-7.24.0.zip");
        return -1;
    }
    //获取URL中的文件名
    char* url = argv[1];
    char name[32] = "";
    getfilename(url,name);
     // 调用函数库所提供函数实现的download()函数,  完成文件的下载
    CURLcode res = download(url,name);
    // 根据返回结果,提示用户下载成功与否
    if(CURLE_OK == res)
    {
        printf("great! download %s as %s succesfully.",url,name);
    }
    else
    {
        printf("sorry:( cannot download %s.",url);
    }
    return 0;
}
代码中的注释已经非常详尽了,这里就不再多加解释了。如果看完注释,还是有不明白的地方,或者想更加深入地了解这个程序,
可以参考《C程序设计伴侣》的第七章关于这个例子的介绍。
现在,我们可以用如下命令编译这个程序:
cl /TP get.c
编译后,得到get.exe,也就是我们为下载狂人写的下载程序。(这里的/TP让你觉得有点不舒服? 那么看看学了C语言,如何利用cURL写一个下载程序?——在MinGW环境下实现) 下载程序的开发简单,其 使用就更简单了,比如我们要下载cURL的源代码, 可以使用下面的命令:
get.exe http://curl.haxx.se/download/curl-7.28.1.zip
利用cURL,不到一百行代码,我们轻松地完成了一个下载程序,当然,如果你有兴趣,还可以进一步完善,比如加上进度提示等等。 这个事例也告诉我们一个道理:
学会站在巨人的肩膀上,可以收到事半功倍的效果
我们介绍了如何在Windows平台的Visual C++环境下利用cURL快速高效地实现一个下载程序。但是有朋友反馈说,“这是在用C++代码冒充C代码”。这位朋友说的很有道理,这是因为 Visual C++对C语言的支持并不十分完善,为了使用C语言中的一些新特性,我在编译最终代码的时候,加上了一个/TP选项,这实际上是将.c文件当作.cpp文 件来编译了,也就是把C语言代码视作了C++代码。这里需要说明的是,我们在代码中用到的全是C语言,只是在编译的时候采取了一些曲线救国的策略。

虽然用/TP的方式可以解决问题,但是看起来总有些旁门左道的感觉。幸运的是,在Windows平台上,不只有Visual C++这一款编译器,还有其他的C语言编译器,比如,MInGW就是其中应用比较广泛的一款。利用MinGW,我们同样可以完成整个过程,同时还更加简 单。

  1. 编译libcurl函数库     在Visual C++环境下,我们是使用其nmake程序进行编译,而在MinGW环境下,我们使用其make程序编译(这里的前提是MinGW安装目录下的bin目录在系统PATH路径中)。首先进入cURL的源代码目录,然后用如下命令编译:      make mingw32      因为我们在这里只是使用cURL的基本功能,所以没有使用其他附加选项。
  2. 编译结果     很快我们就可以得到我们需要的编译结果(位于lib目录下):
  • libcurl.a 静态链接库文件 
  • libcurldll.a 动态链接库的导入库
  • libcurl.dll 动态链接库
安装     MinGW环境下的安装同Visual C++环境下的安装相似:      头文件:将include目录下的curl目录复制到MinGW的头文件目录,比如:E:\MinGW\include      库文件:将上面编译得到的libcurl.a和libcurldll.a复制到MinGW的库文件目录,比如:E:\MinGW\lib      dll文件:将上面编译得到的libcurl.dll复制到系统PATH目录,比如,C:\Windows\System32使用     仍然使用我们在学了C语言,如何利用cURL写一个下载程序?——用cURL提供的API实现下载中介绍到的get.c文件,用下面的命令进行编译:      动态链接:gcc –o get.exe get.c –lcurldll      静态链接:gcc –o get.exe get.c –lcurl      这样,我们就得到了下载程序get.exe。

整体来看,MinGW环境下libcurl的使用跟在Visual C++环境下的使用是相似的,只是MinGW对C语言的支持根据完善,看起来更加名门正派一些。总之,两种环境下都可以实现,而具体使用哪一种,就看你自己的喜好了。

转自:http://www.howzhi.com/course/3387/lesson/43112