赞
踩
用来告诉 libcurl 执行什么样的动作,该函数有 3 个参数(该函数的可设置选项非常多):
第 1 个参数 handle 是由 curl_easy_init() 返回的句柄;第 2 个参数是可以设置的选项(CURLoption);第 3 个参数是与第 2 个选项相关联的参数,参数类型必须由选项值(CURLoption)来确定。
这里只介绍与本问题相关的两个选项:
CURLOPT_CONNECTTIMEOUT:允许与服务器建立连接的最长时间(以秒为单位)。
CURLOPT_TIMEOUT:允许 cURL 函数执行的最长时间(以秒为单位)。请注意,此设置的值应包括 CURLOPT_CONNECTTIMEOUT 的值。
也就是说,CURLOPT_CONNECTTIMEOUT 是 CURLOPT_TIMEOUT 所代表的时间段,所以CURLOPT_TIMEOUT的值应该大于CURLOPT_CONNECTTIMEOUT的值。
用途:当设定的超时时间到期时,强制退出 curl_easy_perform 函数。
以阻塞的方式执行全部的请求,当执行完以后或者失败了才返回。
代码中使用 libcurl 库,完成本机设备与服务器的文件上传下载等工作。
在调用 curl_easy_setopt 设置了一系列的参数后,调用 curl_easy_perform 接口,进程崩溃。
从调用 curl_easy_perform,到进程崩溃,这个过程所用的时间,恰好与通过 curl_easy_setopt 设定的超时时间一致,代码大致流程如下所示:
- #include <stdio.h>
- #include <curl/curl.h>
-
- void main(void)
- {
- CURLcode return_code;
- CURL *curl;
- char resbuf[256] = {0};
-
- return_code = curl_global_init(CURL_GLOBAL_ALL);
- if (CURLE_OK != return_code) {
- printf("Error: curl_global_init.\n");
- return;
- }
-
- curl = curl_easy_init();
- if (NULL == curl) {
- printf("Error: curl_easy_init.\n");
- goto CLEAN;
- }
-
- curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 3); //连接超时限制
- curl_easy_setopt(curl, CURLOPT_TIMEOUT, 20); //整个CURL操作的超时限制
- curl_easy_setopt(curl, CURLOPT_URL, "http://www.xxxx.com"); //待访问地址
- curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); //显示CURL库详细的操作信息
-
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteData); //回调函数
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, resbuf); //数据缓存区
-
- DBG_printf("Start perform GET:0\n");
-
- return_code= curl_easy_perform(curl);
- if (CURLE_OK != return_code) {
- DBG_printf("get curl_easy_perform() failed: %s\n", curl_easy_strerror(return_code));
- goto CLEAN;
- }
-
- DBG_printf("End perform GET:0\n");
-
- CLEAN:
- curl_global_cleanup();
- }
可以看到,代码中设置了连接超时时间3s,CURL操作超时时间20s。(其实任意设置一个就可以验证该问题;如果都设置,则超时时间为 CURLOPT_CONNECTTIMEOUT 设置的时间)
当超时时间到期时,curl_easy_perform 函数被强制退出,程序自动崩溃。而当去掉了超时限制之后,进程不崩溃了,并且报错如图所示内容:
可以看到,curl_easy_perform 函数的执行,共计耗时 1715097046 - 1715096706 = 40(s) 时间,这是 curl_easy_perform 函数自己内部的处理超时时长(其实是 gethostbyname_r 所用时长,下面会讲到),接下来根据 libcurl 的源码来进一步分析。
根据打印日志信息搜索 libcurl 源码:
Curl_ipv4_resolve_r 接口中,调用了 gethostbyname_r 函数;该函数负责把传入的域名解析为对应的 IP 地址。
当网络不稳定时,会导致 gethostbyname_r 接口无法立即返回,并且在 gethostbyname_r 的 40s超时之后自动退出。(博主这里验证是40s,暂不知为何为该值)
所以就大胆得出结论:libcurl 库中,curl_easy_perform 首先会调用到 gethostbyname_r 函数来获取主机 IP 地址,当网络不稳定时,gethostbyname_r 会有阻塞40s超时机制,如果在gethostbyname_r 阻塞过程中,curl_easy_perform 退出了,则会导致 libcurl 库异常,进而造成程序崩溃。
去掉超时限制这种解决方法的弊端是:curl_easy_perform 是阻塞调用的,去掉超时限制后,虽然进程不会崩溃了,但进程会在 curl_easy_perfor 函数除阻塞40s,导致无法及时执行后面的代码。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。