前言
之前未曾用过libcurl,只是在postman文档中见过http访问接口是通过libcurl实现的,这次看到同事用其实现的ftp,顿时觉得libcurl在通信中,还是挺万能的,所以我想学一下。
我查过资料后发现它真的支持很多协议:libcurl当前支持http, https, ftp, gopher, telnet, dict, file, 和ldap 协议。还有,它是一个跨平台的网络协议库,在基于LibCurl的程序里,主要采用callback function (回调函数)的形式完成传输任务,用户在启动传输前设置好各类参数和回调函数,当满足条件时libcurl将调用用户的回调函数实现特定功能。
学习libcurl,其实最好看一下官方的文档:libcurl - programming tutorial
基础知识介绍
libcurl有两种工作模式:easy interface,此模式是同步,阻塞式的传输,单个传输;
multi interface,此模式是在单个线程同时进行多个传输,它有多线程的优点,但是有不需要承担多线程管理的责任,很是方便。大家可根据不同的需求,选取不同的模式。
easy模式的流程
- curl_global_init(long flags) :libcurl初始化,在任何平台使用libcurl的方法之前,必须要用此函数进行先初始化。唯一的参数是为了设置如何初始化的,有以下几个值:默认CURL_GLOBAL_DEFAULT是CURL_GLOBAL_ALL,即初始化所有已知的全部子模块;CURL_GLOBAL_WIN32,只能在windows系统上使用,它会初始化win32 socket ;CURL_GLOBAL_SSL,它会初始化ssl库。
#define CURL_GLOBAL_SSL (1<<0)
#define CURL_GLOBAL_WIN32 (1<<1)
#define CURL_GLOBAL_ALL (CURL_GLOBAL_SSL|CURL_GLOBAL_WIN32)
#define CURL_GLOBAL_DEFAULT CURL_GLOBAL_ALL
- 获取操作句柄。根据自己的需求,选用不同的模式,获取对应的句柄。
m_pHndCurlEasy = curl_easy_init();
- 传输时,通过curl_easy_setopt设置基本参数,如url地址、http头、cookie信息、发送超时时间等,其中,CURLOPT_URL是必设的选项,格式如下。 第二个参数不同值决定设置不同的参数类型,具体可参考博客:curl库中curl_easy_setopt函数详解_I_O_fly的博客-CSDN博客_curl_easy_setopt
curl_easy_setopt(handle, CURLOPT_URL, "http://domain.com/");
- 设置完成后,调用curl_easy_perform(CURL *curl)函数发送数据,我理解为“开始表演了!!”:基本配置已配置好,开始运转了;
- 数据发送完毕后,调用curl_easy_cleanup清空句柄;
- 不再使用libcurl后,调用curl_global_cleanup()做清理工作。也可以说一个curl_global_init对应一个curl_global_cleanup。
官方示例
官方示例的网址是:libcurl - source code examples
可以将官方示例下载下来,看看在各种场合的用法。
这里我贴一个ftp通配符下载文件的示例(ftp-wildcard),可以看出与我上方介绍的流程是一致的(easy模式)。
/* <DESC>
* FTP wildcard pattern matching
* </DESC>
*/
#include <curl/curl.h>
#include <stdio.h>
struct callback_data {
FILE *output;
};
static long file_is_coming(struct curl_fileinfo *finfo,
struct callback_data *data,
int remains);
static long file_is_downloaded(struct callback_data *data);
static size_t write_it(char *buff, size_t size, size_t nmemb,
void *cb_data);
int main(int argc, char **argv)
{
/* curl easy handle */
CURL *handle;
/* help data */
struct callback_data data = { 0 };
/* global initialization */
int rc = curl_global_init(CURL_GLOBAL_ALL);
if(rc)
return rc;
/* initialization of easy handle */
handle = curl_easy_init();
if(!handle) {
curl_global_cleanup();
return CURLE_OUT_OF_MEMORY;
}
/* turn on wildcard matching */
curl_easy_setopt(handle, CURLOPT_WILDCARDMATCH, 1L);
/* callback is called before download of concrete file started */
curl_easy_setopt(handle, CURLOPT_CHUNK_BGN_FUNCTION, file_is_coming);
/* callback is called after data from the file have been transferred */
curl_easy_setopt(handle, CURLOPT_CHUNK_END_FUNCTION, file_is_downloaded);
/* this callback will write contents into files */
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_it);
/* put transfer data into callbacks */
curl_easy_setopt(handle, CURLOPT_CHUNK_DATA, &data);
curl_easy_setopt(handle, CURLOPT_WRITEDATA, &data);
/* curl_easy_setopt(handle, CURLOPT_VERBOSE, 1L); */
/* set an URL containing wildcard pattern (only in the last part) */
if(argc == 2)
curl_easy_setopt(handle, CURLOPT_URL, argv[1]);
else
curl_easy_setopt(handle, CURLOPT_URL, "ftp://example.com/test/*");
/* and start transfer! */
rc = curl_easy_perform(handle);
curl_easy_cleanup(handle);
curl_global_cleanup();
return rc;
}
static long file_is_coming(struct curl_fileinfo *finfo,
struct callback_data *data,
int remains)
{
printf("%3d %40s %10luB ", remains, finfo->filename,
(unsigned long)finfo->size);
switch(finfo->filetype) {
case CURLFILETYPE_DIRECTORY:
printf(" DIR\n");
break;
case CURLFILETYPE_FILE:
printf("FILE ");
break;
default:
printf("OTHER\n");
break;
}
if(finfo->filetype == CURLFILETYPE_FILE) {
/* do not transfer files >= 50B */
if(finfo->size > 50) {
printf("SKIPPED\n");
return CURL_CHUNK_BGN_FUNC_SKIP;
}
data->output = fopen(finfo->filename, "wb");
if(!data->output) {
return CURL_CHUNK_BGN_FUNC_FAIL;
}
}
return CURL_CHUNK_BGN_FUNC_OK;
}
static long file_is_downloaded(struct callback_data *data)
{
if(data->output) {
printf("DOWNLOADED\n");
fclose(data->output);
data->output = 0x0;
}
return CURL_CHUNK_END_FUNC_OK;
}
static size_t write_it(char *buff, size_t size, size_t nmemb,
void *cb_data)
{
struct callback_data *data = cb_data;
size_t written = 0;
if(data->output)
written = fwrite(buff, size, nmemb, data->output);
else
/* listing output */
written = fwrite(buff, size, nmemb, stdout);
return written;
}
结束语
easy interface是基本的,也是最简单的用法,后续如果有空的话,写一篇multi interface的。