博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
libcurl使用时疑难问题【如:文件下载】
阅读量:4165 次
发布时间:2019-05-26

本文共 7116 字,大约阅读时间需要 23 分钟。

场景:

1. 下载过程中,遇设备突然断网,在使用libcurl提供的API时,出现阻塞不返回的情况,影响了后续的业务。

问题:

 curl_easy_perform是阻塞的方式进行下载的, curl_easy_perform执行后,程序会在这里阻塞等待下载结束(成功结束或者失败结束).此时若正常下载一段时间后,进行网络中断, curl_easy_perform并不会返回失败,而是阻塞整个程序卡在这里,此时即使网络连接重新恢复, curl_easy_perform也无法恢复继续下载,导致整个程序出现”死机”状态.

之前提供的解决办法:

2.1在下载中,另起一个线程,若发现下载状态卡死(可以通过定期检查文件大小来实现),则从外部中断下载线程.此方法需另起线程,而且直接中断线程,会给整个程序带来不稳定.

2.2.下载过程中,设置超时时间为30秒, 30秒后若下载未完成就重新连接进行下载.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

3、我的解决办法(实际代码):封装libcurl,设置超时时间,以及必要的变量控制

需要注意的设置项

curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L);        //timeout for the connect phase

   /* abort if slower than 1 bytes/sec during 6 seconds */

 curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 1);

 curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME,  6);

头文件:

typedef struct http_set_opt{	int  timeout;	const char *ca_file;	const char *ca_path;	const char *content_type;	const char *digest_value;	const char *token;	void *write_func;	void *write_data;	void *progress_func;	void *progress_data;	const char *upload_file_path;	void *read_func;	void *read_data;	int   read_data_size;}http_set_opt_t;typedef int(*download_file_progress_cbk)(void *lpUserData, int64_t dltotal, int64_t dlnow);typedef struct http_download_param{	http_set_opt_t * http_set_opt;	/*	* request	*/	const char     * serv_url;	const char     * store_path;	/*	* callback	*/	download_file_progress_cbk progress_cbk;	void *                     progress_cbk_userdata;	/*	* attribute	*/	int              running;  // abort: < 0, running: 1	/*	* defaults	*/	char             defaults[16];}http_download_param_t;/*接口*/OpenSDK_API int Open_http_client_download_file(http_download_param_t*download_param);/*回调*/typedef struct {	int(*Open_http_client_download_file)(http_download_param_t *download_param);	}http_client_interface;

CPP文件:

#include 
#include
#include
#include
#include
#include
static size_t download_write_func(char *ptr, size_t size, size_t nmemb, void *userdata){ FILE *fp = (FILE *)userdata; if(fwrite(ptr, size, nmemb, fp) != (size*nmemb)) { fprintf(stderr, "file:<%s> func:<%s> line:<%d>: fwrite failed\n", __FILE__, __FUNCTION__, __LINE__); } return (size*nmemb);}/*回调*/static int http_client_download_file_progress(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow){ http_download_param_t *download_param = (http_download_param_t *)clientp; if (download_param == NULL || download_param->running < 0) //特别注意running的使用,可以快速返回 return -1; if (download_param != NULL && download_param->progress_cbk != NULL){ int64_t totalSize = (int64_t)dltotal; int64_t downloadedSize = (int64_t)dlnow; download_param->progress_cbk(download_param->progress_cbk_userdata, totalSize, downloadedSize); } return 0;}/*实现*/int Open_http_client_download_file(http_download_param_t *download_param){ if (download_param->serv_url == NULL || download_param->store_path == NULL) { fprintf(stderr, "file:<%s> func:<%s> line:<%d>: argument for sky_http_client_download_file should not be NULL\n", __FILE__, __FUNCTION__, __LINE__); return -1; } if (download_param->progress_cbk == NULL) { fprintf(stderr, "file:<%s> func:<%s> line:<%d>: download progress callback func should not be NULL\n", __FILE__, __FUNCTION__, __LINE__); return -1; } long local_file_length = 0;// get_localfile_length(download_param->store_path); //fprintf(stderr, "local file length is: %ld\n", local_file_length); FILE *fp = fopen(download_param->store_path, "wb"); if (fp == NULL) { fprintf(stderr, "file:<%s> func:<%s> line:<%d>: can not open %s\n", __FILE__, __FUNCTION__, __LINE__, download_param->store_path); return -1; } CURL *curl = curl_easy_init(); if (curl == NULL) { fprintf(stderr, "file:<%s> func:<%s> line:<%d>: curl_easy_init failed\n", __FILE__, __FUNCTION__, __LINE__); fclose(fp); return -1; } download_param->running = 1; //注意赋值 /* 1: set http server url */ curl_easy_setopt(curl, CURLOPT_URL, download_param->serv_url); /* 2:set debug opt */#if HTTP_DEBUG curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, http_debug);#endif /* 3: set http request method */ curl_easy_setopt(curl, CURLOPT_HTTPGET, 1); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, download_write_func); curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, http_client_download_file_progress); curl_easy_setopt(curl, CURLOPT_XFERINFODATA, download_param); curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);// curl_easy_setopt(curl, CURLOPT_RESUME_FROM_LARGE, local_file_length); /* abort if slower than 1 bytes/sec during 6 seconds */ /* 注意该此处的设置,断网等情况下的死锁 */ //timeout for the connect phase curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L); /* abort if slower than 1 bytes/sec during 6 seconds */ curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 1L); curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, 6L); curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 1L); curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, 6L); if (download_param->http_set_opt != NULL) { /* 4:set time out by user */ if (download_param->http_set_opt->timeout) { curl_easy_setopt(curl, CURLOPT_TIMEOUT, download_param->http_set_opt->timeout); } /* 5:set CA */ if (download_param->http_set_opt->ca_file) { curl_easy_setopt(curl, CURLOPT_CAINFO, download_param->http_set_opt->ca_file); } if (download_param->http_set_opt->ca_path) { curl_easy_setopt(curl, CURLOPT_CAPATH, download_param->http_set_opt->ca_path); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1); } } else { curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0); } /* 6:set no signal */ curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); CURLcode ret_code = curl_easy_perform(curl); if (ret_code != CURLE_OK) { fprintf(stderr, "file:<%s> func:<%s> line:<%d>: curl_easy_perform failed, error code: %d\n", __FILE__, __FUNCTION__, __LINE__, ret_code); //LOG_WRITE(LOG_LEVEL_ERROR, "curl_easy_perform failed, error code: %d", ret_code); if (ret_code == CURLE_COULDNT_CONNECT || ret_code == CURLE_COULDNT_RESOLVE_HOST || ret_code == CURLE_COULDNT_RESOLVE_PROXY) { fprintf(stderr, "file:<%s> func:<%s> line:<%d>: network problem\n", __FILE__, __FUNCTION__, __LINE__); } else if (ret_code == CURLE_OPERATION_TIMEDOUT){ fprintf(stderr, "file:<%s> func:<%s> line:<%d>: curl_easy_perform time out\n", __FILE__, __FUNCTION__, __LINE__); } else if (ret_code == CURLE_SSL_CONNECT_ERROR) { fprintf(stderr, "file:<%s> func:<%s> line:<%d>: SSL connect error\n", __FILE__, __FUNCTION__, __LINE__); } else if (ret_code == CURLE_SSL_CACERT) { fprintf(stderr, "file:<%s> func:<%s> line:<%d>: CA file error\n", __FILE__, __FUNCTION__, __LINE__); } curl_easy_cleanup(curl); fclose(fp); return -1; } curl_easy_cleanup(curl); fclose(fp); return 0;}

以上为封装的文件下载过程,应用层在使用该接口的时候,需要根据实际的情况,给 http_download_param_t    http_download对象先初始化,memset(&http_download, 0, sizeof(http_download)),在具体的情境下赋值,比如上层的业务突然关闭或者停止,需要将  http_download.running = -1;     这样就内部就能及时地返回结束,而不会使得上层调用 int  result= Open_http_client_download_file(&http_download);接口时被阻塞,不返回;

转载地址:http://acqxi.baihongyu.com/

你可能感兴趣的文章
ACE篇之十:ACE容器之六(映射表管理器)
查看>>
ACE篇之十一:ACE容器之七(自调整的二叉树)
查看>>
基本的TCP/IP Socket用法(一)
查看>>
基本的TCP/IP Socket用法(二)
查看>>
处理事件及多个I/O流--ACE Reactor框架总览
查看>>
SIGINT and others
查看>>
vim编程常用命令(随时补充更新)
查看>>
一个简单的makefile示例及其注释
查看>>
python mysql
查看>>
高效人士的八个习惯
查看>>
mysql 赋给用户权限 grant all privileges on
查看>>
读取文件的几种方法
查看>>
yast 创建本地数据源
查看>>
vim 编码方式(encoding、fileencoding、fileencodings、termencoding介绍)
查看>>
程序员的十层楼
查看>>
windows 下php支持curl
查看>>
获取文件夹文件(C++)
查看>>
判断文件夹是否存在
查看>>
快速的内存分配器
查看>>
java中super 的两种用法
查看>>