下载是使用卷曲C API无法恢复 [英] Download is not resuming using Curl C API

查看:169
本文介绍了下载是使用卷曲C API无法恢复的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述


  1. 我试图恢复已失败,原因是互联网失败的下载。我使用的检查卷曲下载成功的功能是:

      curl_multi_info_read

    这个函数返回正确的错误code( CURLE_COULDNT_CONNECT ),当它被称为第一次,当互联网已经丢失。如果我尝试再次调用它,它返回NULL指针,这意味着任何消息。其实,我现在用的是返回错误code,检查互联网连接是否present与否。这是困扰我,因为它不返回任何错误code在其第二个电话,如果没有互联网。任何一个可以请告诉我如何使用这个功能来检查返回code,因为这个错误code( CURLE_COULDNT_CONNECT )是非常重要的,我在检查网络的状态,并相应地恢复从停止下载当我回到连接...


  2. 为了恢复我使用的下载

      curl_easy_setopt(卷曲,CURLOPT_RESUME_FROM,InternalOffset);

    我调用这个函数每次我得到一个没有网络设置选项,这样,当互联网连接后面的下载就可以恢复...



注释是Daniel Stenberg发明

下面是关于平台和版本的libcurl一些细节:


  • 卷曲版本 - 的libcurl 7.21.6

  • 平台 - 的Linux(Ubuntu的)

评论:


  1. 是的。你的看法是正确的。我从堆栈中删除容易处理,通过设置新的选项( curl_easy_setopt(卷曲,CURLOPT_RESUME_FROM,InternalOffset)),最后我没多进行再次添加到多手柄。它返回正确的错误,如果他们是没有互联网连接。我的问题是:我是否需要重复上述步骤,每个当我失去互联网连接,以获得正确的错误的时间?如果我没有做这些步骤,将 curl_multi_info_read 函数总是返回NULL。


  2. 还有一个观察我做的就是下载开始恢复的,当互联网连接又回来了。它开始从一点上,它停止的地方previously下载。这已经到了我作为一个surpris。是卷曲内部照顾恢复下载时,它回来了互联网。如果这是正确的?我真的需要照顾重启下载或离开卷曲,因为它处理得当?



解决方案

您可能需要提供更多的相关信息。


例如:你没有明确说是否你使用了多接口简单的界面 ..&安培;也许提到你的工作和放什么平台;什么的libcurl 您正在使用等。

下面是最小的卷曲容易对抗多测试的libcurl / 7.21.6

我愉快地猛拉出来的网线,停止HTTP服务器和放大器;等等〜这似乎应付ok了。

这可能会帮助您:

  curl_easy_setopt(卷曲,CURLOPT_LOW_SPEED_LIMIT,dl_lowspeed_bytes); //字节/秒
curl_easy_setopt(卷曲,CURLOPT_LOW_SPEED_TIME,dl_lowspeed_time); //秒
curl_easy_setopt(卷曲,CURLOPT_VERBOSE,1L);


注:你必须非常努力工作来的卷曲翻倒,当连接滴出。这是由设计。但让人惊奇一些。



我怀疑你会想使用 CURLOPT_TIMEOUT 。这将超时转移。如果你的D / L是大那么它几乎肯定会需要更长的时间比你可能ppared等待,以找出是否有东西与您的网络连接〜>超时会被击中$ P $。相比之下, CURLOPT_LOW_SPEED_TIME 超时可能永远不会被击中,甚至atfter经过的传输时间小时。



curltest_easy.c:

  / * ------------------------------------ ----------------
curltest_easy.c
警告:仅用于测试目的〜
* /
#包括LT&;&stdio.h中GT;
#包括LT&;&unistd.h中GT;
#包括LT&;卷曲/ curl.h>
#包括LT&;卷曲/ types.h中>
#包括LT&;卷曲/ easy.h>
#包括LT&; SYS / stat.h>静态INT dl_progress(无效* clientp,双dltotal,双dlnow,双ultotal,双ulnow)
{
    如果(dlnow&安培;&安培; dltotal)
        的printf(DL:3.0F%%% \\ r,即100 * dlnow / dltotal); //婶子PROG-MON
    fflush(标准输出);
    返回0;
}静态为size_t dl_write(void *的缓冲区,为size_t长度size_t nmemb个,无效*流)
{
    返回FWRITE(缓冲区大小,nmemb个,(FILE *)流);
}
INT do_dl(无效)
{
    卷曲*卷曲;
    FILE * FP;
    卷曲code curl_retval;
    长HTTP_RESPONSE;
    双dl_size;
    INT RETVAL = 0;
    长dl_lowspeed_bytes = 1000; // 1K
    长dl_lowspeed_time = 10; //秒
    / *放在这里的东西大,preferably,你可以随意关掉一台服务器上;)* /
    字符的URL [] = {http://fc00.deviantart.net/fs26/f/2008/134/1/a/Dragon_VII_by_NegativeFeedback.swf};
    字符文件名[] = {blah.dl};    struct stat中ST = {0};
    如果(STAT(文件名,和放大器;!ST));
    的printf(st.st_size:[%LD] \\ n,st.st_size);
    如果(!(FP = FOPEN(文件名,AB)))/ *追加二进制* /
      返回1;
    curl_global_init(CURL_GLOBAL_DEFAULT);
    卷曲= curl_easy_init();    如果(卷曲)
    {
        //http://linux.die.net/man/3/curl_easy_setopt
        curl_easy_setopt(卷曲,CURLOPT_URL,URL);        / * *回调/
        curl_easy_setopt(卷曲,CURLOPT_WRITEFUNCTION,dl_write);
        curl_easy_setopt(卷曲,CURLOPT_PROGRESSFUNCTION,dl_progress);
        curl_easy_setopt(卷曲,CURLOPT_NOPROGRESS,0);        / *卷曲将继续运行 - 所以你可以自由恢复
        从网络断开等以自己的方式,而不
        distrubing手头的卷曲任务。 **这是由设计:P ** * /
        // curl_easy_setopt(卷曲,CURLOPT_TIMEOUT,60);
        // curl_easy_setopt(卷曲,CURLOPT_CONNECTTIMEOUT,30);
        / *设立分下载速度阈值和放大器;中止之前经历的时间* /
        curl_easy_setopt(卷曲,CURLOPT_LOW_SPEED_LIMIT,dl_lowspeed_bytes); //字节/秒
        curl_easy_setopt(卷曲,CURLOPT_LOW_SPEED_TIME,dl_lowspeed_time); //秒,而中止低于前低限spped
        curl_easy_setopt(卷曲,CURLOPT_WRITEDATA,FP);
        curl_easy_setopt(卷曲,CURLOPT_RESUME_FROM,st.st_size);        / *取消注释这让卷曲告诉你其高达* /
        // curl_easy_setopt(卷曲,CURLOPT_VERBOSE,1L);
        如果(CURLE_OK!=(curl_retval = curl_easy_perform(卷曲)))
        {
            的printf(curl_retval:[%D]。\\ n,curl_retval);
            开关(curl_retval)
            {
                //传输的部分文件
                案例CURLE_WRITE_ERROR://可能是由于连接断开
                打破;                //全部卷曲/ curl.h定义                默认://建议放弃了未处理的错误
                RETVAL = 0;
            };
            curl_easy_getinfo(卷曲,CURLINFO_CONTENT_LENGTH_DOWNLOAD,&安培; dl_size);
            的printf(CURLINFO_CONTENT_LENGTH_DOWNLOAD:%F \\ N,dl_size);
            curl_retval = curl_easy_getinfo(卷曲,CURLINFO_RESPONSE_ code,&安培; HTTP_RESPONSE);            //看到:http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
            的printf(CURLINFO_RESPONSE_ code:%ld个\\ N,HTTP_RESPONSE);            开关(HTTP_RESPONSE)
            {
            情况下0:从开球//例如,连接上下〜建议重试,直到一些最大限制
            打破;            案例200://耶,我们至少得到了我们的网址
            打破;            案例206:
            案例416://http://www.checkupdown.com/status/E416.html
            的printf(!哎哟,你可能要处理这个问题和放大器;其他的\\ n);            默认://建议放弃了未处理的错误
            RETVAL = 0;
            };
        }
        其他
        {
            的printf(我们在这里的工作已经完成;)\\ n);
            RETVAL = 2;
        }
        如果(FP)
            FCLOSE(FP);        如果(卷曲)
            curl_easy_cleanup(卷曲);
    }    的printf(RETVAL内容[%d] \\ n,RETVAL);
    返回RETVAL;
}
INT主要(无效)
{
    而(!do_dl())
    {
        usleep(5000);
    }    返回0;
}/ *注----$ sudo易于得到安装libcurl4-GNUTLS-dev的
$卷曲配置--libs
-L / usr / lib目录/ I386-Linux的GNU -lcurl -Wl,-Bsymbolic函数#oook。我们开始做吧:
$ gcc的-o curltest_easy curltest_easy.c -L / usr / lib目录/ I386-Linux的GNU -lcurl -Wl,-Bsymbolic函数
$ ./curltest
* /




curltest_multi.c:

  / * ------------------------------------ ----------------
curltest_mult1.c
警告:仅用于测试目的〜
* /
#包括LT&;&stdio.h中GT;
#包括LT&;&unistd.h中GT;
#包括LT&;卷曲/ curl.h>
#包括LT&;卷曲/ types.h中>
#包括LT&;卷曲/ easy.h>
#包括LT&; SYS / stat.h>typedef结构S_dl_byte_data
{
    双new_bytes_received; //从最新的请求
    双existing_filesize;
} dl_byte_data,* pdl_byte_data;静态INT dl_progress(pdl_byte_data PDATA,双dltotal,双dlnow,双ultotal,双ulnow)
{
    / * dltotal:获取内容长度的=哈克的样子〜少哈克将第一
    做一个HEAD请求和放大器;然后用curl_easy_getinfo * CURLINFO_CONTENT_LENGTH_DOWNLOAD /
    如果(dltotal&安培;&安培; dlnow)
    {
        pdata-> new_bytes_received = dlnow;
        dltotal + = pdata-> existing_filesize;
        dlnow + = pdata-> existing_filesize;
        的printf(DL:3.0F%总%%:%0F接受:%0F \\河,100 * dlnow / dltotal,dltotal,dlnow); //婶子PROG-MON
        fflush(标准输出);
    }
    返回0;
}静态为size_t dl_write(void *的缓冲区,为size_t长度size_t nmemb个,无效*流)
{
    返回FWRITE(缓冲区大小,nmemb个,(FILE *)流);
}////////////////////////
INT do_dl(无效)
{
    CURLM * multi_handle;
    卷曲*卷曲;
    FILE * FP;
    卷曲code curl_retval;
    INT RETVAL = 0;
    INT handle_count = 0;
    双dl_bytes_remaining,dl_bytes_received;
    dl_byte_data st_dldata = {0};
    焦炭curl_error_buf [CURL_ERROR_SIZE] = {咩};
    长dl_lowspeed_bytes = 1000,dl_lowspeed_time = 10; / *,持续10秒1KBs * /    / *放在这里的东西大,preferably,你可以随意关掉一台服务器上;)* /
    字符的URL [] = {http://fc00.deviantart.net/fs26/f/2008/134/1/a/Dragon_VII_by_NegativeFeedback.swf};    烧焦outfilename [] = {blah.swf},文件名[] = {blah.dl};
    struct stat中ST = {0};
    如果((FP = FOPEN(文件名,AB))|| == -1 FSTAT(的fileno(FP),放大器;!ST))//追加二进制
      返回-1;    如果(curl_global_init(CURL_GLOBAL_DEFAULT))
      返回-2;    如果(!(multi_handle = curl_multi_init()))
      返回-3;    如果(!(卷曲= curl_easy_init()))
      返回-4;
    st_dldata.new_bytes_received = st_dldata.existing_filesize = st.st_size;    //http://curl.haxx.se/libcurl/c/curl_easy_setopt.html
    curl_easy_setopt(卷曲,CURLOPT_URL,URL);    / * *回调/
    curl_easy_setopt(卷曲,CURLOPT_WRITEFUNCTION,dl_write);
    curl_easy_setopt(卷曲,CURLOPT_PROGRESSFUNCTION,dl_progress);
    curl_easy_setopt(卷曲,CURLOPT_PROGRESSDATA,&安培; st_dldata);
    curl_easy_setopt(卷曲,CURLOPT_NOPROGRESS,0);    / *卷曲将继续运行 - 所以,你可以自由地从网络断开等恢复
    在没有手distrubing卷曲任务自己的路。 **这是由设计:P **
    该follwoing设立分下载速度阈值和放大器;中止之前经历的时间* /
    curl_easy_setopt(卷曲,CURLOPT_LOW_SPEED_LIMIT,dl_lowspeed_bytes); //字节/秒
    curl_easy_setopt(卷曲,CURLOPT_LOW_SPEED_TIME,dl_lowspeed_time); //秒,而中止低于前低限spped
    //替代地这些libcurl中7.25可用
    // curl_easy_setopt(卷曲,CURLOPT_TCP_KEEPALIVE,1L);
    // curl_easy_setopt(卷曲,CURLOPT_TCP_KEEPIDLE,10);
    // curl_easy_setopt(卷曲,CURLOPT_TCP_KEEPINTVL,10);    curl_easy_setopt(卷曲,CURLOPT_WRITEDATA,FP);    / *取消注释这让卷曲告诉你其高达* /
    // curl_easy_setopt(卷曲,CURLOPT_VERBOSE,1L);    curl_easy_setopt(卷曲,CURLOPT_ERRORBUFFER,curl_error_buf);
    做
    {
        如果(st_dldata.new_bytes_received)//设置新范围的部分转移,如果我们有previously收到一些字节
        {
            的printf(恢复D / L .. \\ n);
            fflush(FP);
            //获取新的文件大小和放大器;健全性检查文件;出错退出外do循环和放大器;返回到主
            如果(-1 ==(RETVAL = FSTAT(的fileno(FP),放大器; ST))||(st_dldata.existing_filesize = st.st_size)!)打破;
            //还看到:CURLOPT_RANGE传递一个字符串,用我们自己的X-Y系列
            curl_easy_setopt(卷曲,CURLOPT_RESUME_FROM,st.st_size);
            st_dldata.new_bytes_received = 0;
        }
        (已收到\\ n \\的nbytes:[%0F] \\ n,st_dldata.existing_filesize)printf的;        //再次与放大器重新使用curl句柄;再次和放大器;再次和放大器;再次...哈哈
        curl_multi_add_handle(multi_handle,卷曲);        做// curl_multi_perform事件循环
        {
            CURLMsg * PMSG;
            INT msgs_in_queue;            而(CURLM_CALL_MULTI_PERFORM == curl_multi_perform(multi_handle,&安培; handle_count));            //任何mesages检查,无论句柄计数的
            而(PMSG = curl_multi_info_read(multi_handle,&安培; msgs_in_queue))
            {
                长HTTP_RESPONSE;                的printf(\\ nmsgs_in_queue:[%D]。\\ n,msgs_in_queue);
                如果(CURLMSG_DONE = pMsg->!MSG)
                {
                    fprintf中(标准错误,CURLMSG_DONE = pMsg->味精:[%D]。\\ n,pMsg->味精);
                }
                其他
                {
                    输出(pMsg-> data.result:[%D]。意思是:[%S] \\ n,pMsg-> data.result,curl_easy_strerror(pMsg-> data.result));
                    如果(CURLE_OK = pMsg-> data.result!)的printf(curl_error_buf:[%S] \\ n,curl_error_buf);
                    开关(pMsg-> data.result)
                    {
                    案例CURLE_OK://///////////////////////////////////////////// ////////////////////////////////////////
                    的printf(CURLE_OK:);
                    curl_easy_getinfo(pMsg-> easy_handle,CURLINFO_CONTENT_LENGTH_DOWNLOAD,&安培; dl_bytes_remaining);
                    curl_easy_getinfo(pMsg-> easy_handle,CURLINFO_SIZE_DOWNLOAD,&安培; dl_bytes_received);
                    如果(dl_bytes_remaining == dl_bytes_received)
                    {
                        的printf(我们在这里的工作已经完成;)\\ n);
                        重命名(文件名,outfilename);
                        RETVAL = 1;
                    }
                    其他
                    {
                        的printf(!哎哟st_dldata.new_bytes_received [%F] \\ n,st_dldata.new_bytes_received);
                        的printf(!哎哟dl_bytes_received [%F] dl_bytes_remaining [%F] \\ n,dl_bytes_received,dl_bytes_remaining);
                        RETVAL = dl_bytes_received< dl_bytes_remaining? 0:-5;
                    }
                    打破; ////////////////////////////////////////////////// ///////////////////////////////////////////////                    案例CURLE_COULDNT_CONNECT://没有网络连接?
                    案例CURLE_OPERATION_TIMEDOUT:CURLOPT_LOW_SPEED_TIME的// COS
                    案例CURLE_COULDNT_RESOLVE_HOST://主机/ DNS下来?
                    的printf(CURMESSAGE开关handle_count:[%D]。\\ n,handle_count);
                    打破; //我们会继续努力                    默认://看到:http://curl.haxx.se/libcurl/c/libcurl-errors.html
                    handle_count = 0;
                    RETVAL = -5;
                    };
                    //看到:http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
                    curl_retval = curl_easy_getinfo(pMsg-> easy_handle,CURLINFO_RESPONSE_ code,&安培; HTTP_RESPONSE);
                    的printf(CURLINFO_RESPONSE_ code HTTP:[%LD] \\ n,HTTP_RESPONSE);
                    开关(HTTP_RESPONSE)
                    {
                    情况下0:从开球//例如,连接上下〜建议重试,直到一些最大限制
                    案例200://耶,我们至少得到了我们的网址
                    案例206://部分内容
                    打破;                    案例416:
                    //不能D / L范围内无论〜COS没有服务器支持
                    //或COS我们要求一个无效的范围,即〜:我们已经D / LD文件
                    的printf(HTTP416:或者D / L已经完成或HTTP服务器无法D / L范围\\ n);
                    RETVAL = 2;                    默认://建议放弃了未处理的错误
                    handle_count = 0;
                    RETVAL = -6;
                    };
                }
            }            如果(handle_count)//选择任何活动手柄
            {
                FD_SET FD_READ = {0},FD_WRITE = {0},fd_excep = {0};
                timeval结构超时= {5,0};
                INT select_retval;
                INT fd_max;                curl_multi_fdset(multi_handle,&安培; FD_READ,&安培; FD_WRITE,和放大器; fd_excep,&安培; fd_max);
                如果(-1 ==(select_retval =选择(fd_max + 1,&安培; FD_READ,&安培; FD_WRITE,和放大器; fd_excep,和放大器;超时)))
                {
                    //错误号应设置以指示错误
                    fprintf中(标准错误,亚克西选择错误:(\\ n);
                    handle_count = 0;
                    RETVAL = -7;
                    打破;
                }
                其他{/ *检查什么* /}
            }        }而(handle_count);        curl_multi_remove_handle(multi_handle,卷曲);
        的printf(从这里下去?);
        的getchar();
    }
    而(RETVAL == 0);    curl_multi_cleanup(multi_handle);
    curl_easy_cleanup(卷曲);
    curl_global_cleanup();
    如果(FP)FCLOSE(FP);    返回RETVAL;
}////////////////////////
INT主要(无效)
{
    INT RETVAL;
    的printf(\\ n \\ ncurl_multi D / L〜测试版卷曲:[%S] \\ n,curl_version());
    而(1!=(RETVAL = do_dl()))
    {
        的printf(RETVAL内容[%d]继续\\ n \\ n吗?,RETVAL);
        的printf(是否继续?);
        的getchar();
    }
    的printf(测试\\ NEND \\ n \\ n!,RETVAL);
    返回RETVAL;
}/ *注----$ sudo易于得到安装libcurl4-GNUTLS-dev的
$卷曲配置--libs
-L / usr / lib目录/ I386-Linux的GNU -lcurl -Wl,-Bsymbolic函数#oook。我们开始做吧:
$ gcc的-o curltest_multi curltest_multi.c -L / usr / lib目录/ I386-Linux的GNU -lcurl -Wl,-Bsymbolic函数
$。/ curltest_multi* /

呃,你可能要记住开始一个全新的测试之前删除 blah.dl 文件。在PROG故意没有,所以你可以先截断现有文件进行测试;)

注:这样的事情,你也许应该*不能仅仅依靠 CURLE_COULDNT_CONNECT 〜您的codeS应主要错误处理洛尔(;如果你的PROG严格供个人使用的可能较少;)




我已经更新了curtest_multi.c展示 easy_handle 再利用。

的注意来自的的文件建立


  

当完成单个转让,容易手柄仍留有
  添加到多堆。您需要先卸下手柄方便
  与curl_multi_remove_handle(3),然后用其关闭
  curl_easy_cleanup(3),或可能设置新的选项,并添加
  再次curl_multi_add_handle(3)启动另一个传输。


希望这有助于;)

  1. I am trying to resume a download that has failed due to internet failure. The function I am using to check the success of a curl download is:

    curl_multi_info_read
    

    This function returns the proper error code (CURLE_COULDNT_CONNECT) when it is called the first time when internet has lost. If I try to call it again, it returns NULL pointer which means no messages. Actually, I am using the return error code to check whether internet connection is present or not. This is troubling me as it doesn't return any error code on its second call if there is no internet. Can any one please tell me how to make use of this function to check return code, because this error code (CURLE_COULDNT_CONNECT) is very important to me in checking status of internet and accordingly to resume download from where it stopped when I got back the connection ....

  2. In order to resume download I am using

    curl_easy_setopt (curl, CURLOPT_RESUME_FROM, InternalOffset);
    

    I am calling this function to set option each time I get a lost internet connection so that the download can resume when internet connection is back ...


Notes to Daniel Stenberg:

Here are some details about platform and libcurl version:

  • curl version - libcurl 7.21.6
  • platform - Linux (Ubuntu)

Comments:

  1. Yes. Your view is right. I removed the easy handle from stack, added again to multi handle by setting new option (curl_easy_setopt(curl, CURLOPT_RESUME_FROM, InternalOffset)) and finally I did multi perform. It returned proper error if their is no internet connection. My question is: Do I need to repeat the above steps each time when I lost internet connection to get proper error? If I don't do these steps, will curl_multi_info_read function always return NULL.

  2. One more observation I made is download starts resuming as and when internet connection is back. It starts downloading from the point it where it stopped previously. This has come to me as a surpris . Is curl internally taking care of resuming the download when it gets back the internet. If Is this right? Do I really need to take care resuming the download or leave to curl as it handles it properly?

解决方案

You might need to provide more infos.

Eg: you don't explicitly say whether you're using the multi interface or the easy interface.. & maybe mention what platform you're working on & what libcurl you are using etc.

Below are minimal curl easy and multi tests against libcurl/7.21.6.
I have happily yanked out the network cables, stopped http servers & so on ~ it seems to cope ok.

These might help you:

curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, dl_lowspeed_bytes); //bytes/sec
curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, dl_lowspeed_time); //seconds
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);


NB: you have to work quite hard to make curl fall over when the connection drops out. this is by design. but comes as a surprise to some.

[Edit:]
I doubt you would want to be using CURLOPT_TIMEOUT. This would timeout the transfer. If your d/l is large then it will almost certainly take longer than you may be prepared to wait to find out if there's something up with your network connection ~> the timeout would get hit. By contrast, the CURLOPT_LOW_SPEED_TIME timeout may never get hit, even atfter hours of elapsed transfer time.


curltest_easy.c:

/*----------------------------------------------------
curltest_easy.c 
WARNING: for test purposes only ~ 
*/
#include <stdio.h>
#include <unistd.h>
#include <curl/curl.h>
#include <curl/types.h>
#include <curl/easy.h>
#include <sys/stat.h>



static int dl_progress(void *clientp,double dltotal,double dlnow,double ultotal,double ulnow)
{
    if (dlnow && dltotal)
        printf("dl:%3.0f%%\r",100*dlnow/dltotal); //shenzi prog-mon 
    fflush(stdout);    
    return 0;
}

static size_t dl_write(void *buffer, size_t size, size_t nmemb, void *stream)
{    
    return fwrite(buffer, size, nmemb, (FILE*)stream); 
}


int do_dl(void) 
{
    CURL *curl;
    FILE *fp;
    CURLcode curl_retval;
    long http_response;
    double dl_size;
    int retval=0;
    long dl_lowspeed_bytes=1000; //1K
    long dl_lowspeed_time=10; //sec        
    /*put something biG here, preferably on a server that you can switch off at will ;) */
    char url[] = {"http://fc00.deviantart.net/fs26/f/2008/134/1/a/Dragon_VII_by_NegativeFeedback.swf"};
    char filename[]={"blah.dl"};

    struct stat st={0};    
    if (!stat(filename, &st));    
    printf("st.st_size:[%ld]\n", st.st_size);  


    if(!(fp=fopen(filename, "ab"))) /*append binary*/
      return 1; 


    curl_global_init(CURL_GLOBAL_DEFAULT);   
    curl = curl_easy_init();

    if (curl) 
    {   
        //http://linux.die.net/man/3/curl_easy_setopt
        curl_easy_setopt(curl, CURLOPT_URL, url);

        /*callbacks*/
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, dl_write);
        curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, dl_progress);
        curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);

        /*curl will keep running -so you have the freedom to recover 
        from network disconnects etc in your own way without
        distrubing the curl task in hand. ** this is by design :p ** */ 
        //curl_easy_setopt(curl, CURLOPT_TIMEOUT, 60);          
        //curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30);
        /*set up min download speed threshold & time endured before aborting*/
        curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, dl_lowspeed_bytes); //bytes/sec
        curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, dl_lowspeed_time); //seconds while below low spped limit before aborting


        curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
        curl_easy_setopt(curl, CURLOPT_RESUME_FROM,st.st_size);

        /*uncomment this to get curl to tell you what its up to*/
        //curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);


        if(CURLE_OK != (curl_retval=curl_easy_perform(curl)))
        {                      
            printf("curl_retval:[%d]\n", curl_retval);
            switch(curl_retval) 
            {
                //Transferred a partial file
                case CURLE_WRITE_ERROR: //can be due to a dropped connection
                break;

                //all defined in curl/curl.h 

                default: //suggest quitting on unhandled error
                retval=0;
            };    


            curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &dl_size);
            printf("CURLINFO_CONTENT_LENGTH_DOWNLOAD:%f\n", dl_size);


            curl_retval=curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_response);

            //see: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
            printf("CURLINFO_RESPONSE_CODE:%ld\n", http_response);

            switch(http_response)
            {
            case 0: //eg connection down  from kick-off ~suggest retrying till some max limit
            break;

            case 200: //yay we at least got to our url
            break;

            case 206:
            case 416: //http://www.checkupdown.com/status/E416.html
            printf("ouch! you might want to handle this & others\n"); 

            default: //suggest quitting on an unhandled error
            retval=0;
            };            
        }
        else
        {
            printf("our work here is done ;)\n");
            retval=2;
        }


        if (fp)
            fclose(fp);

        if (curl)
            curl_easy_cleanup(curl);
    }

    printf("retval [%d]\n", retval);
    return retval;
}


int main(void) 
{
    while (!do_dl())
    {
        usleep(5000);
    }

    return 0;
}

/* notes ----

$sudo apt-get install libcurl4-gnutls-dev
$ curl-config --libs
-L/usr/lib/i386-linux-gnu -lcurl -Wl,-Bsymbolic-functions

#oook. lets do it:
$ gcc -o curltest_easy curltest_easy.c -L/usr/lib/i386-linux-gnu -lcurl -Wl,-Bsymbolic-functions
$ ./curltest
*/



curltest_multi.c:

/*----------------------------------------------------
curltest_mult1.c
WARNING: for test purposes only ~
*/
#include <stdio.h>
#include <unistd.h>
#include <curl/curl.h>
#include <curl/types.h>
#include <curl/easy.h>
#include <sys/stat.h>

typedef struct S_dl_byte_data
{
    double new_bytes_received;  //from the latest request
    double existing_filesize;
} dl_byte_data, *pdl_byte_data;

static int dl_progress(pdl_byte_data pdata,double dltotal,double dlnow,double ultotal,double ulnow)
{
    /*dltotal := hacky way of getting the Content-Length ~ less hacky would be to first
    do a HEAD request & then curl_easy_getinfo with CURLINFO_CONTENT_LENGTH_DOWNLOAD*/
    if (dltotal && dlnow)
    {
        pdata->new_bytes_received=dlnow;
        dltotal+=pdata->existing_filesize;
        dlnow+=pdata->existing_filesize;
        printf(" dl:%3.0f%% total:%.0f received:%.0f\r",100*dlnow/dltotal, dltotal, dlnow); //shenzi prog-mon
        fflush(stdout);
    }
    return 0;
}

static size_t dl_write(void *buffer, size_t size, size_t nmemb, void *stream)
{
    return fwrite(buffer, size, nmemb, (FILE*)stream);
}

////////////////////////
int do_dl(void)
{
    CURLM *multi_handle;
    CURL *curl;
    FILE *fp;
    CURLcode curl_retval;
    int retval=0;
    int handle_count=0;
    double dl_bytes_remaining, dl_bytes_received;
    dl_byte_data st_dldata={0};
    char curl_error_buf[CURL_ERROR_SIZE]={"meh"};
    long dl_lowspeed_bytes=1000, dl_lowspeed_time=10; /* 1KBs for 10 secs*/

    /*put something biG here, preferably on a server that you can switch off at will ;) */
    char url[] = {"http://fc00.deviantart.net/fs26/f/2008/134/1/a/Dragon_VII_by_NegativeFeedback.swf"};

    char outfilename[]={"blah.swf"}, filename[]={"blah.dl"};
    struct stat st={0};


    if (!(fp=fopen(filename, "ab")) || -1==fstat(fileno(fp), &st)) //append binary
      return -1;

    if (curl_global_init(CURL_GLOBAL_DEFAULT))
      return -2;

    if (!(multi_handle = curl_multi_init()))
      return -3;

    if (!(curl = curl_easy_init()))
      return -4;


    st_dldata.new_bytes_received=st_dldata.existing_filesize=st.st_size;

    //http://curl.haxx.se/libcurl/c/curl_easy_setopt.html
    curl_easy_setopt(curl, CURLOPT_URL, url);

    /*callbacks*/
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, dl_write);
    curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, dl_progress);
    curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &st_dldata);
    curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);

    /*curl will keep running -so you have the freedom to recover from network disconnects etc
    in your own way without distrubing the curl task in hand. ** this is by design :p **
    The follwoing sets up min download speed threshold & time endured before aborting*/
    curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, dl_lowspeed_bytes); //bytes/sec
    curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, dl_lowspeed_time); //seconds while below low spped limit before aborting
    //alternatively these are available in libcurl 7.25
    //curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE,1L);
    //curl_easy_setopt(curl, CURLOPT_TCP_KEEPIDLE,10);
    //curl_easy_setopt(curl, CURLOPT_TCP_KEEPINTVL,10);

    curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);

    /*uncomment this to get curl to tell you what its up to*/
    //curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);

    curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_error_buf);


    do
    {
        if (st_dldata.new_bytes_received) //set the new range for the partial transfer if we have previously received some bytes 
        {
            printf("resuming d/l..\n");
            fflush(fp);
            //get the new filesize & sanity check for file; on error quit outer do-loop & return to main
            if (-1==(retval=fstat(fileno(fp), &st)) || !(st_dldata.existing_filesize=st.st_size)) break; 
            //see also: CURLOPT_RANGE for passing a string with our own X-Y range
            curl_easy_setopt(curl, CURLOPT_RESUME_FROM, st.st_size);
            st_dldata.new_bytes_received=0;
        }
        printf("\n\nbytes already received:[%.0f]\n", st_dldata.existing_filesize);

        //re-use the curl handle again & again & again & again... lol
        curl_multi_add_handle(multi_handle, curl);

        do //curl_multi_perform event-loop
        {
            CURLMsg *pMsg;
            int msgs_in_queue;

            while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(multi_handle, &handle_count));

            //check for any mesages regardless of handle count
            while(pMsg=curl_multi_info_read(multi_handle, &msgs_in_queue))
            {
                long http_response;

                printf("\nmsgs_in_queue:[%d]\n",msgs_in_queue);
                if (CURLMSG_DONE != pMsg->msg)
                {
                    fprintf(stderr,"CURLMSG_DONE != pMsg->msg:[%d]\n", pMsg->msg);
                }
                else
                {
                    printf("pMsg->data.result:[%d] meaning:[%s]\n",pMsg->data.result,curl_easy_strerror(pMsg->data.result));
                    if (CURLE_OK != pMsg->data.result) printf("curl_error_buf:[%s]\n", curl_error_buf);
                    switch(pMsg->data.result)
                    {
                    case CURLE_OK: ///////////////////////////////////////////////////////////////////////////////////////
                    printf("CURLE_OK: ");
                    curl_easy_getinfo(pMsg->easy_handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &dl_bytes_remaining);
                    curl_easy_getinfo(pMsg->easy_handle, CURLINFO_SIZE_DOWNLOAD, &dl_bytes_received);
                    if (dl_bytes_remaining == dl_bytes_received)
                    {
                        printf("our work here is done ;)\n");
                        rename(filename, outfilename);
                        retval=1;
                    }
                    else
                    {
                        printf("ouch! st_dldata.new_bytes_received[%f]\n",st_dldata.new_bytes_received);
                        printf("ouch! dl_bytes_received[%f] dl_bytes_remaining[%f]\n",dl_bytes_received,dl_bytes_remaining);
                        retval=dl_bytes_received < dl_bytes_remaining ? 0 : -5;
                    }
                    break; /////////////////////////////////////////////////////////////////////////////////////////////////

                    case CURLE_COULDNT_CONNECT:      //no network connectivity ?
                    case CURLE_OPERATION_TIMEDOUT:   //cos of CURLOPT_LOW_SPEED_TIME
                    case CURLE_COULDNT_RESOLVE_HOST: //host/DNS down ?
                    printf("CURMESSAGE switch handle_count:[%d]\n",handle_count);
                    break; //we'll keep trying

                    default://see: http://curl.haxx.se/libcurl/c/libcurl-errors.html
                    handle_count=0;
                    retval=-5;
                    };


                    //see: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
                    curl_retval=curl_easy_getinfo(pMsg->easy_handle, CURLINFO_RESPONSE_CODE, &http_response);
                    printf("CURLINFO_RESPONSE_CODE HTTP:[%ld]\n", http_response);
                    switch(http_response)
                    {
                    case 0:   //eg connection down  from kick-off ~suggest retrying till some max limit
                    case 200: //yay we at least got to our url
                    case 206: //Partial Content
                    break;

                    case 416:
                    //cannot d/l range ~ either cos no server support
                    //or cos we're asking for an invalid range ~ie: we already d/ld the file
                    printf("HTTP416: either the d/l is already complete or the http server cannot d/l a range\n");
                    retval=2;

                    default: //suggest quitting on an unhandled error
                    handle_count=0;
                    retval=-6;
                    };
                }
            }

            if (handle_count) //select on any active handles
            {
                fd_set fd_read={0}, fd_write={0}, fd_excep={0};
                struct timeval timeout={5,0};
                int select_retval;
                int fd_max;

                curl_multi_fdset(multi_handle, &fd_read, &fd_write, &fd_excep, &fd_max);
                if (-1 == (select_retval=select(fd_max+1, &fd_read, &fd_write, &fd_excep, &timeout)))
                {
                    //errno shall be set to indicate the error
                    fprintf(stderr, "yikes! select error :(\n");
                    handle_count=0;
                    retval=-7;
                    break;
                }
                else{/*check whatever*/}
            }

        } while (handle_count);

        curl_multi_remove_handle(multi_handle,curl);
        printf("continue from here?");
        getchar();        
    }
    while(retval==0);

    curl_multi_cleanup(multi_handle);
    curl_easy_cleanup(curl);
    curl_global_cleanup();
    if (fp) fclose(fp);

    return retval;
}

////////////////////////
int main(void)
{
    int retval;
    printf("\n\ncurl_multi d/l test ~curl version:[%s]\n", curl_version());
    while (1!=(retval=do_dl()))
    {
        printf("retval [%d] continue?\n\n", retval);
        printf("continue?");
        getchar();
    }
    printf("\nend of test!\n\n", retval);
    return retval;
}

/* notes ----

$sudo apt-get install libcurl4-gnutls-dev
$curl-config --libs
-L/usr/lib/i386-linux-gnu -lcurl -Wl,-Bsymbolic-functions

#oook. lets do it:
$gcc -o curltest_multi curltest_multi.c -L/usr/lib/i386-linux-gnu -lcurl -Wl,-Bsymbolic-functions
$./curltest_multi

*/

Erm, you might want to remember to delete the blah.dl file before starting a brand new test. the prog deliberately does not, so you can truncate an existing file beforehand for testing ;)

NB: for something like this you maybe should *not just rely on CURLE_COULDNT_CONNECT ~your codes should be mostly error handling lol (;possibly less if your prog is strictly for personal use;)


[Edit:] I have updated the curtest_multi.c to demonstrate easy_handle re-use.

And do note the following quote from the documentaion:

When a single transfer is completed, the easy handle is still left added to the multi stack. You need to first remove the easy handle with curl_multi_remove_handle(3) and then close it with curl_easy_cleanup(3), or possibly set new options to it and add it again with curl_multi_add_handle(3) to start another transfer.

hope this helps ;)

这篇关于下载是使用卷曲C API无法恢复的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆