libcurl库的使用的一些心得
一、概述
测试做一个下载工具,最开始想到了libcurl库,和它提供的一些接口。
二、libcurl提供的一些api的使用
1、CURLcode curl_global_init(long flags);----全局初始化函数,在windows下使用CURL_GLOBAL_WIN32,初始化win sock的一些信息。
2、CURL *curl_easy_init();-----初始化一个CURL*的句柄。
3、CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...);----设置http的一些属性,设置给libcurl
4、CURLM *curl_multi_init(void);------初始化一个CURLM * 句柄,使用这个可以实现非阻塞模式,建议使用该函数
5、CURLMcode curl_multi_add_handle(CURLM *multi_handle, CURL *curl_handle);----把CURL*句柄添加到CURLM * 中
6、CURLcode curl_easy_perform(CURL *curl);----执行下载或者上传任务,执行的是函数curl_easy_setopt设置的一些选项
7、CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles);-----执行下载或者上传任务,通过curl_multi_add_handle加入到CURLM*句柄中的各个任务。主要使用的是select非阻塞模式,通过这个可以比较容易的实现多线程任务。
8、void curl_easy_cleanup(CURL *curl);----清理CURL*句柄。
9、CURLMcode curl_multi_cleanup(CURLM *multi_handle);----清理CURLM*句柄。
10、void curl_global_cleanup(void);----清理全局初始化的堆栈操作。
注:最好看官网提供的一些英文资料和它提供的一些例子,这些东西比在网上随便找的资料有用的多。
三、设计思想。
1、对于一些大文件,创建一些线程去执行下载任务。----- 最主要的就是如何让各个线程从指定位置开始下载和到指定位置停止下载的问题。
2、如何实现下载,可以通过curl_easy_setopt函数设置一个回调函数完成对本地文件的写入,完成下载工作。---下载或其他任务,会在curl_easy_perform或curl_multi_perform函数调用后执行。
3、对于大文件,获取到http(或其他服务器)上的文件大小后,根据需要创建的线程数,给每个线程分配固定的数据段进行下载。
4、等各个文件分块下载完成后,把下载的文件块拼装起来,组合成需要的下载文件。----完成所有下载工作(这部分工作应该是最简单的)。
四、一些代码实例
1、从http头获取到下载文件的大小(我在网上没有找到实例)
/*
* get file size from http head data
*/
size_t getcontentlengthfunc(void *ptr, size_t size, size_t nmemb, void *stream)
{
int r;
long len = 0;
/* _snscanf() is Win32 specific */
// r = _snscanf(ptr, size * nmemb, "Content-Length: %ld\n", &len);
r = sscanf((const char*)ptr, "Content-Length: %ld\n", &len);
if (r) /* Microsoft: we don't read the specs */
*((long *) stream) = len;
return size * nmemb;
}
size_t write_func(void* , size_t , size_t , void* )
{
return 0;
}
int32 getdownloadfilesize(const char* url)
{
#if WIN32
curl_global_init(CURL_GLOBAL_WIN32);
#else
curl_global_init(CURL_GLOBAL_ALL);
#endif
CURL* hcurl = curl_easy_init();
curl_easy_setopt(hcurl, CURLOPT_URL, url);
curl_easy_setopt(hcurl, CURLOPT_FOLLOWLOCATION, 1L); // 重定向
curl_easy_setopt(hcurl, CURLOPT_CONNECTTIMEOUT, TIME_OUT); // set connect timeout
long filesize = 0;
/* set http head call back function */
curl_easy_setopt(hcurl, CURLOPT_HEADERFUNCTION, getcontentlengthfunc);
curl_easy_setopt(hcurl, CURLOPT_HEADERDATA, &filesize);
FILE* fp = fopen("test.txt", "ab+");
if(!fp)
return 0;
curl_easy_setopt(hcurl, CURLOPT_WRITEDATA, fp);
curl_easy_setopt(hcurl, CURLOPT_WRITEFUNCTION, write_func);
curl_easy_perform(hcurl); // 函数会执行失败哦,不过没关系,我们已经从得到了filesize的大小了。
fclose(fp);
system("if exist test.txt del test.txt");
// 清理工作
curl_easy_cleanup(hcurl);
curl_global_cleanup();
return (int32)filesize;
}
2、设置断点续传。
void setcurlopt()
{
curl_off_t local_file_len = -1 ;
struct stat file_info;
bool flag = false;
// g_loopsize是分段的大小
curl_off_t nbeginsize = (nthreadcode - 1) * g_loopsize;
/* get local file size */
if(stat(m_strfile.c_str(), &file_info) == 0)
{
local_file_len = file_info.st_size;
nbeginsize += (int32)local_file_len;
flag = true;
}
/* open file use append model */
FILE* fp = fopen(m_strfile.c_str(), "ab+");
if (fp == NULL) {
perror(NULL);
return;
}
/*
*把需要文件开始下载的位置传给libcurl,多线程分段也主要
* 是通过该函数设置下载部分,这里的参数类型必须为curl_off_t ,否则会得不到http的相应
*/
curl_easy_setopt(m_hcurl, CURLOPT_RESUME_FROM_LARGE, flag?local_file_len,0);
// 把文件指针传给libcurl,用户可以根据需求自己设计回调和需要传递的指针
SDownloadFile* file = xxx;
curl_easy_setopt(m_hcurl, CURLOPT_WRITEDATA, file);
// 设置回调函数,writefunc是一个特殊的回调函数,我用多线程分段下载的
curl_easy_setopt(m_hcurl, CURLOPT_WRITEFUNCTION, wirtefunc);
}
/*
* save the downloaded data to defined file
* 这里特别说明一下,分段去调用最后可能出现一个错误信息:failed write body(xxx!=xxxx)
* 这个可以忽略,具体请查看libcurl源码部分。
*/
size_t wirtefunc(void *ptr, size_t size, size_t nmemb, void *stream)
{
SDownloadFile* pinfo = (SDownloadFile*)stream;
assert(pinfo);
if(NULL == pinfo)
return 0;
uint32 ret = 0;
int32 nLave = pinfo->nBeginLen + size * nmemb - pinfo->nEndLen;
if(nLave > 0) // one part download success
{
fflush(pinfo->fp);
fwrite(ptr, size, pinfo->nEndLen - pinfo->nBeginLen, pinfo->fp);
fflush(pinfo->fp);
pinfo->nBeginLen = pinfo->nEndLen;
}
else
{
assert(pinfo->nBeginLen + size * nmemb < (uint32)pinfo->nEndLen);
ret = fwrite(ptr, size, nmemb, pinfo->fp);
pinfo->nBeginLen += ret;
}
return ret;
}
typedef struct _SDownloadFile
{
FILE* fp;
string strfilename;
int32 nBeginLen;
int32 nEndLen;
int32 ncode;
}SDownloadFile;
typedef signed char int8;
typedef unsigned char uint8;
typedef signed short int16;
typedef unsigned short uint16;
typedef signed int int32;
typedef unsigned int uint32;
typedef signed long long int64;
typedef unsigned long long uint64;
3、关于文件组合。
把各个部分已二进制方式读出来,然后写入到一个新的文件中,并删除那些下载的部分文件。
-