libcurl使用时疑难问题【如:文件下载】

场景:

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 <curl.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <string.h>

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);接口时被阻塞,不返回;

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
libcurl 是一个非常强大的开源网络,它提供了在各种操作系统上进行文件下载的功能。使用 libcurl 可以实现简单且有效的文件下载。下面是一个使用 libcurl 下载文件的示例: 1. 引入 libcurl 头文件: ```c #include <curl/curl.h> ``` 2. 定义回调函数: 这个回调函数会在下载数据被调用,我们可以在这个函数中处理下载的数据。 ```c size_t write_callback(void *contents, size_t size, size_t nmemb, void *userp) { size_t realsize = size * nmemb; FILE *file = (FILE *)userp; if (file != NULL) { fwrite(contents, size, nmemb, file); } return realsize; } ``` 3. 执行下载: 在主函数中,我们可以使用 libcurl 提供的函数进行下载。 ```c int main(void) { CURL *curl; FILE *file; curl = curl_easy_init(); if (curl) { // 设置下载的 URL curl_easy_setopt(curl, CURLOPT_URL, "http://example.com/file.txt"); // 打开文件用于保存下载的数据 file = fopen("file.txt", "wb"); if (file != NULL) { // 设置回调函数 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, file); // 执行下载 curl_easy_perform(curl); // 关闭文件 fclose(file); } // 清理资源 curl_easy_cleanup(curl); } return 0; } ``` 以上是使用 libcurl 下载文件的简单示例,它能够通过指定的 URL 下载文件,并将文件保存在本地。使用 libcurl 还可以设置代理、设置下载进度回调等更多功能,使得文件下载功能更加强大和灵活。通过 libcurl,我们可以轻松地在我们的应用程序中实现文件下载功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值