当前位置:   article > 正文

调用libcurl库的curl_easy_perform函数后,程序崩溃_libcurl请求崩溃

libcurl请求崩溃

预备知识:

curl_easy_setopt

用来告诉 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 函数

curl_easy_perform

以阻塞的方式执行全部的请求,当执行完以后或者失败了才返回。

问题背景:

代码中使用 libcurl 库,完成本机设备与服务器的文件上传下载等工作。

在调用 curl_easy_setopt 设置了一系列的参数后,调用 curl_easy_perform 接口,进程崩溃。

问题分析:

从调用 curl_easy_perform,到进程崩溃,这个过程所用的时间,恰好与通过 curl_easy_setopt 设定的超时时间一致,代码大致流程如下所示:

  1. #include <stdio.h>
  2. #include <curl/curl.h>
  3. void main(void)
  4. {
  5. CURLcode return_code;
  6. CURL *curl;
  7. char resbuf[256] = {0};
  8. return_code = curl_global_init(CURL_GLOBAL_ALL);
  9. if (CURLE_OK != return_code) {
  10. printf("Error: curl_global_init.\n");
  11. return;
  12. }
  13. curl = curl_easy_init();
  14. if (NULL == curl) {
  15. printf("Error: curl_easy_init.\n");
  16. goto CLEAN;
  17. }
  18. curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 3); //连接超时限制
  19. curl_easy_setopt(curl, CURLOPT_TIMEOUT, 20); //整个CURL操作的超时限制
  20. curl_easy_setopt(curl, CURLOPT_URL, "http://www.xxxx.com"); //待访问地址
  21. curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); //显示CURL库详细的操作信息
  22. curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteData); //回调函数
  23. curl_easy_setopt(curl, CURLOPT_WRITEDATA, resbuf); //数据缓存区
  24. DBG_printf("Start perform GET:0\n");
  25. return_code= curl_easy_perform(curl);
  26. if (CURLE_OK != return_code) {
  27. DBG_printf("get curl_easy_perform() failed: %s\n", curl_easy_strerror(return_code));
  28. goto CLEAN;
  29. }
  30. DBG_printf("End perform GET:0\n");
  31. CLEAN:
  32. curl_global_cleanup();
  33. }

可以看到,代码中设置了连接超时时间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,导致无法及时执行后面的代码。  

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/天景科技苑/article/detail/968651
推荐阅读
相关标签
  

闽ICP备14008679号