借着curl 7.75.0版本更新, 最近又下载下来玩了玩, 在此做个简单记录

1.环境搭建

首先是libcurl动态库, 自己下载源码编译的话如果要使用https协议还要下载OpenSSL和libssh的源码一起编译, 我嫌麻烦, 所以直接官网下载的官方编译好的动态库

linux一般自带的有或者直接apt get都很方便了

这里放个windows环境的下载地址 : https://curl.se/windows/

红框部分是curl部分功能的依赖库, 这里我建议都下载下来扔到项目目录里

下载下来解压后curl目录结构如上图, 其中bin放的是动态库, lib是静态库, include里是头文件, 需要提及的是lib中两个静态库都是.a结尾的, 较小且带dll的应该是windows版本的, 我在编译自己的程序时将这个静态库名称改成了libcurl.lib

最后我将有可能用到的动态库, 静态库, 证书, 头文件整合了一下, 内容如下:

之后在自己的程序中链接libcurl, 包含curl目录下的头文件, 将dll放在可执行程序同目录下就可以开始使用了.

2. 调用接口进行http通讯

下面先列一下curl请求的基本流程和重要变量

(1) CURLcode curl_global_init() : 该接口用于初始化curl库, 应该在所有curl操作之前被调用

(2) CURL* curl_easy_init() : 该接口返回一个curl句柄, 类型为CURL*, 一次会话的相关操作都在这个返回句柄上进行

(3) void curl_easy_cleanup(CURL*) : 该接口用于释放给定curl句柄, 每一次会话结束都应该调用此接口释放对应的curl句柄

(4) CURLcode curl_easy_setopt(CURL*, CURLoption, ...) : 该接口通过传入不同的宏可以设置指定curl句柄的相关属性, 以此控制会话的各种属性内容

这里给一个官方链接可以查询OPT的含义, 其中也包含官方的example : https://curl.se/libcurl/c/curl_easy_setopt.html

(5) CURLcode curl_easy_perform(CURL*) : 通过给定句柄执行通讯会话

(6) CURLcode : 几乎所有的curl接口的返回值都为此类型, 这个code定义了所有curl操作时的状态, 这里给一个官方链接可以查询code的含义 : https://curl.se/libcurl/c/libcurl-errors.html

(7) const char* curl_easy_strerror(CURLcode) : 将CURLcode转为对应含义的字符串方便进行日志输出等操作

以上是我经常使用到的curl接口, curl功能强大, 支持的协议与内容远不止http/https

官方自己给出的评价是 : libcurl is probably the most portable, most powerful and most often used network transfer library on this planet.

这里我封装了两个功能, 分别是http GET请求网页和http GET下载文件, 过程中启用了cookie.

上代码 :

自己封装的curl类

 1 class CHttpClient
2 {
3 public:
4 CHttpClient();
5 ~CHttpClient();
6
7 long http_enable_cookie(const char *path);
8 long http_post(const char *url);
9 long http_submit(const char *url, std::vector<std::string> &form);
10 long http_get(const char *url, std::string &body);
11 long http_download(const char *url, const char *fullpath);
12 long http_add_header(const char *header);
13 long http_add_multi_header(std::vector<std::string> &list);
14
15 private:
16 bool prepare_curl(const char *url);
17 bool exec_curl();
18 bool try_cleanup_curl();
19
20 private:
21 CURL *m_pCurl;
22 struct curl_slist *m_pHeader;
23 bool m_bSetCookie;
24 bool m_bSetHeader;
25 char m_szCookiePath[MAX_PATH];
26 };

最主要的curl_global_init放在了构造函数中, 这里不再展示, 其中prepare_curl, exec_curl, try_cleanup_curl为我对curl http通讯流程的基本封装

下面展示上述三个接口

 1 bool CHttpClient::prepare_curl(const char *url)
2 {
3 m_pCurl = curl_easy_init();
4 if (nullptr == m_pCurl) return false;
5
6 curl_easy_setopt(m_pCurl, CURLOPT_URL, url);
7 curl_easy_setopt(m_pCurl, CURLOPT_FOLLOWLOCATION, 1L);
8 curl_easy_setopt(m_pCurl, CURLOPT_SSL_VERIFYPEER, 0L);
9 curl_easy_setopt(m_pCurl, CURLOPT_SSL_VERIFYHOST, 0L);
10
11 if (m_bSetHeader)
12 {
13 curl_easy_setopt(m_pCurl, CURLOPT_HTTPHEADER, m_pHeader);
14 }
15
16 if (m_bSetCookie)
17 {
18 curl_easy_setopt(m_pCurl, CURLOPT_COOKIEJAR, m_szCookiePath); //set-cookie将会修改此路径对应cookie缓存文件
19 curl_easy_setopt(m_pCurl, CURLOPT_COOKIEFILE, m_szCookiePath); //发送请求时将会从此文件中读取cookie
20 }
21
22 #ifdef DEBUG
23 curl_easy_setopt(m_pCurl, CURLOPT_VERBOSE, 1L);
24 curl_easy_setopt(m_pCurl, CURLOPT_DEBUGFUNCTION, cb_dbg);
25 #endif
26
27 return true;
28 }
29
30 bool CHttpClient::exec_curl()
31 {
32 CURLcode retCode = curl_easy_perform(m_pCurl);
33 try_cleanup_curl();
34
35 #ifdef DEBUG
36 print_dbg_msg();
37 #endif
38
39 if (CURLE_OK != retCode)
40 {
41 LOG_MSG(LOG_ERROR, "curl execute with code[%d] msg[%s]", retCode, curl_easy_strerror(retCode));
42 return false;
43 }
44 return true;
45 }
46
47 bool CHttpClient::try_cleanup_curl()
48 {
49 if (nullptr != m_pCurl)
50 {
51 curl_easy_cleanup(m_pCurl);
52 m_pCurl = nullptr;
53 }
54
55 if (m_bSetHeader)
56 {
57 curl_slist_free_all(m_pHeader);
58 m_pHeader = nullptr;
59 m_bSetHeader = false;
60 }
61
62 return true;
63 }

prepare_curl主要进行curl句柄的初始化, 设置http通用的参数

exec_curl执行curl通讯, 通讯完成后调用try_cleanup_curl进行内存释放, 并打印debug通讯信息

针对prepare_curl中curl_easy_setopt的参数, 这里展开解释一下

(1)CURLOPT_URL : http通讯的地址, 可以解析域名

(2)CURLOPT_FOLLOWLOCATION : 跟随网页重定向

(3)CURLOPT_SSL_VERIFYPEER & CURLOPT_SSL_VERIFYHOST : 双端是否进行SSL安全验证, 此处我把这个功能关掉了, 正常生产环境是不会这样做的, curl库中也带的有证书, 老版本可能需要更新一下证书防止有些网页不能访问, 这里我只做调试, 就比较随意了

(4)CURLOPT_HTTPHEADER : 设置http header, 这里传入curl_slist结构体, 使用curl_slist_append可以直接把const char*类型字符串加入这个结构体, 如果不设置, curl默认请求头只有GET, Accept,Host

(5)CURLOPT_COOKIEJAR : 指定本次通讯cookie保存的路径, 保存操作在对应的curl句柄执行curl_easy_cleanup时执行

(6)CURLOPT_COOKIEFILe : 指定本次通讯cookie读取的路径

(7)CURLOPT_VERBOSE : 设置是否回显通讯内容, 开启后如果不指定回调函数, 则使用stderr

(8)CURLOPT_DEBUGFUNCTION : 设置回显时调用的回调函数, 回调函数的参数列表应为(CURL *curl, curl_infotype type, char *data, size_t size, void *usr_ptr), 其中type指示了当前data的类型, 类型包括CURLINFO_TEXT, CURLINFO_HEADER_IN, CURLINFO_HEADER_OUT, CURLINFO_DATA_IN, CURLINFO_DATA_OUT, CURLINFO_SSL_DATA_IN, CURLINFO_SSL_DATA_OUT, CURLINFO_END, 具体含义参考官方文档实际调试一下比较好理解

下面展示CURLOPT_DEBUGFUNCTION对应的回调函数以及print_dbg_msg打印函数

 1 static std::string g_sHeaderOut;
2 static std::string g_sHeaderIn;
3 static std::string g_sDataOut;
4 static std::string g_sDataIn;
5
6 int cb_dbg(CURL *curl, curl_infotype type, char *data, size_t size, void *usr_ptr)
7 {
8 switch (type)
9 {
10 case CURLINFO_HEADER_OUT:
11 g_sHeaderOut.append(data, size);
12 break;
13 case CURLINFO_DATA_OUT:
14 g_sDataOut.append(data, size);
15 break;
16 case CURLINFO_HEADER_IN:
17 g_sHeaderIn.append(data, size);
18 break;
19 case CURLINFO_DATA_IN:
20 g_sDataIn.append(data, size);
21 break;
22 default:
23 break;
24 }
25 return 0;
26 }
27
28 void print_dbg_msg()
29 {
30 if (!g_sHeaderOut.empty())
31 {
32 str_replace(g_sHeaderOut, "%", "%%");
33 LOG_MSG(LOG_DEBUG, "%s", g_sHeaderOut.c_str());
34 g_sHeaderOut.clear();
35 }
36
37 if (!g_sDataOut.empty())
38 {
39 str_replace(g_sDataOut, "%", "%%");
40 LOG_MSG(LOG_DEBUG, "%s", g_sDataOut.c_str());
41 g_sDataOut.clear();
42 }
43
44 if (!g_sHeaderIn.empty())
45 {
46 str_replace(g_sHeaderIn, "%", "%%");
47 LOG_MSG(LOG_DEBUG, "%s", g_sHeaderIn.c_str());
48 g_sHeaderIn.clear();
49 }
50
51 if (!g_sDataIn.empty())
52 {
53 str_replace(g_sDataIn, "%", "%%");
54 LOG_MSG(LOG_DEBUG, "%s", g_sDataIn.c_str());
55 g_sDataIn.clear();
56 }
57 }

这里因为我自己写的日志打印使用vsprinf遇到%会报错, 这里我又封装了一个string的replace函数把%替换成%%, 打印的时候可能不太美观, 暂时还没花时间优化

下面展示GET请求和GET download请求

 1 long CHttpClient::http_get(const char *url, std::string &res)
2 {
3 if (!prepare_curl(url)) return TSI_INTERNAL_ERR;
4
5 // CURLOPT_WRITEDATA后的参数会传给回调函数的usrdata
6 curl_easy_setopt(m_pCurl, CURLOPT_WRITEFUNCTION, cb_get);
7 curl_easy_setopt(m_pCurl, CURLOPT_WRITEDATA, &res);
8
9 if (!exec_curl())
10 {
11 LOG_MSG(LOG_ERROR, "http get fail");
12 return TSI_INTERNAL_ERR;
13 }
14 return TSI_NO_ERR;
15 }
16
17 long CHttpClient::http_download(const char *url, const char *fullpath)
18 {
19 if (!prepare_curl(url)) return TSI_INTERNAL_ERR;
20
21 //二进制写入模式创建下载文件
22 FILE *download_file = fopen(fullpath, "wb");
23 if (nullptr == download_file)
24 {
25 try_cleanup_curl();
26 return TSI_INTERNAL_ERR;
27 }
28
29 //将文件句柄设置到下载回调中, curl内部会将大文件分割并多次调用回调写入数据
30 curl_easy_setopt(m_pCurl, CURLOPT_WRITEFUNCTION, cb_download);
31 curl_easy_setopt(m_pCurl, CURLOPT_WRITEDATA, download_file);
32
33 //TODO:要确定一下下载过程是否是异步的, 防止文件还没下载完毕, 后面就fclose了
34 if (!exec_curl())
35 {
36 LOG_MSG(LOG_ERROR, "http download [%s] fail", fullpath);
37 fclose(download_file);
38 std::remove(fullpath);
39 return TSI_INTERNAL_ERR;
40 }
41
42 LOG_MSG(LOG_INFO, "http download [%s] success", fullpath);
43 fclose(download_file);
44 return TSI_NO_ERR;
45 }

其中主要涉及两个CURLOPT, 此处展开解释

(1)CURLOPT_WRITEFUNCTION : 该参数指定get请求到的内容的写入方法, curl默认使用fwrite, 该回调函数参数列表必须为(char *data, size_t size, size_t nmemb, void *usrdata)

(2)CURLOPT_WRITEDATA : 该参数将后跟的数据作为参数传入指定的writefunction中

下面展示两个回调函数cb_get和cb_download

 1 size_t cb_get(char *data, size_t size, size_t nmemb, void *usrdata)
2 {
3 size_t data_size = size * nmemb;
4 static_cast<std::string*>(usrdata)->append(data, data_size);
5 return data_size;
6 }
7
8 size_t cb_download(char *data, size_t size, size_t nmemb, void *usrdata)
9 {
10 size_t data_size = size * nmemb;
11 fwrite(data, size, nmemb, (FILE*)usrdata);
12 return data_size;
13 }

因为download功能涉及具体网站的分析, 这里就不展示调试内容了

以上是http get请求的简单实例, 常用功能应该还有form POST, 暂时没写, 有空补上.

如有错误疏漏, 请务必指出, 十分感谢, 同时欢迎一起探讨相关问题, 转载请注明, 感谢!

C++使用libcurl进行http通讯的更多相关文章

  1. C++ 用libcurl库进行http通讯网络编程

    使用libcurl完成http通讯,很方便而且是线程安全,转载一篇比较好的入门文章 转载自 http://www.cnblogs.com/moodlxs/archive/2012/10/15/2724 ...

  2. 【swupdate文档 三】SWUpdate: 嵌入式系统的软件升级

    SWUpdate: 嵌入式系统的软件升级 概述 本项目被认为有助于从存储媒体或网络更新嵌入式系统.但是,它应该主要作为一个框架来考虑,在这个框架中可以方便地向应用程序添加更多的协议或安装程序(在SWU ...

  3. (转)利用libcurl和国内著名的两个物联网云端通讯的例程, ubuntu和openwrt下调试成功(四)

    1. libcurl 的参考文档如下 CURLOPT_HEADERFUNCTION Pass a pointer to a function that matches the following pr ...

  4. C/C++ 用libcurl库进行http通讯网络编程

    C/C++ 用libcurl库进行http通讯网络编程 目录索引: 一.LibCurl基本编程框架 二.一些基本的函数 三.curl_easy_setopt函数部分选项介绍 四.curl_easy_p ...

  5. C++ 用libcurl库进行http通讯网络编程 【转】

    http://www.cnblogs.com/moodlxs/archive/2012/10/15/2724318.html C++ 用libcurl库进行http通讯网络编程 目录索引: 一.Lib ...

  6. 客户端技术的一点思考(数据存储用SQLite, XMPP通讯用Gloox, Web交互用LibCurl, 数据打包用Protocol Buffer, socket通讯用boost asio)

    今天看到CSDN上这么一篇< 彻底放弃没落的MFC,对新人的忠告!>, 作为一个一直在Windows上搞客户端开发的C++程序员,几年前也有过类似的隐忧(参见 落伍的感觉), 现在却有一些 ...

  7. C++ 用libcurl库进行http通讯网络编程(转)

    转载:http://www.cnblogs.com/moodlxs/archive/2012/10/15/2724318.html 目录索引: 一.LibCurl基本编程框架 二.一些基本的函数 三. ...

  8. C++ 用libcurl库进行http通讯网络编程[转]

    http://www.cnblogs.com/moodlxs/archive/2012/10/15/2724318.html 目录索引: 一.LibCurl基本编程框架 二.一些基本的函数 三.cur ...

  9. C++ 用libcurl库进行http 网络通讯编程

      一.LibCurl基本编程框架libcurl是一个跨平台的网络协议库,支持http, https, ftp, gopher, telnet, dict, file, 和ldap 协议.libcur ...

随机推荐

  1. Java中把对象、对象bean、list集合、对象数组、Map和Set以及字符串转换成Json

    对象转换为Json 对象bean转换为Json List集合转换为Json 对象数组转换为Json Map集合转换为Json Set集合转为Json 字符串转换为Json 把Java对常用的一些数据转 ...

  2. 笔趣看小说Python3爬虫抓取

    笔趣看小说Python3爬虫抓取 获取HTML信息 解析HTML信息 整合代码 获取HTML信息 # -*- coding:UTF-8 -*- import requests if __name__ ...

  3. eclipse下执行maprdeuc程序报错 java.lang.ClassNotFoundException

    最近遇到一个问题,不知怎么突然运行hadoop的map程序报错,困扰了我很久,现在来给大家分享分享.. 错误信息 2017-05-18 21:34:22,104 INFO [main] client. ...

  4. 由于Java的简单类型不能够精确的对浮点数进行运算,这个工具类提供精 确的浮点数运算,包括加减乘除和四舍五入。

    package com.minxinloan.utils; import java.math.BigDecimal; public class Arith { // 源文件Arith.java: /* ...

  5. 环境变量设置,为什么要设置,tomcat的为什么不用设置?

    在学习JAVA的过程中,涉及到多个环境变量(environment variable)的概念,如PATH.正确地配置这些环境变量,是能够顺利学习.开发的前提.而经常出现的问题是:有的学习者能够按照提示 ...

  6. 动态代理+静态代理+cglib代理 详解

    代理定义:代理(Proxy):是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能. 动态代理+静态 ...

  7. ACM 模板库

    Template For ACM 一. 字符串 标准库 sscanf sscanf(const char *__source, const char *__format, ...) :从字符串 __s ...

  8. 2013-2014 ACM-ICPC, NEERC, Eastern Subregional Contest PART (8/10)

    $$2013-2014\ ACM-ICPC,\ NEERC,\ Eastern\ Subregional\ Contest$$ \(A.Podracing\) 首先枚举各个折现上的点,找出最小宽度,然 ...

  9. 灯光照射,圆形探测类问题(解题报告)<分层差分><cmath取整>

    题目描述 一个n*n的网格图上有m个探测器,每个探测器有个探测半径r,问这n*n个点中有多少个点能被探测到. 输入输出格式 输入格式: (1<=r<n<=5000) (1<=m ...

  10. 【uva 1395】Slim Span(图论--最小生成树+结构体快速赋值 模版题)

    题意:给一个N(N<=100)个点的联通图(无自环和平行边),求苗条度(最大边-最小边的值)尽量小的生成树. 解法:枚举+Kruskal.先从小到大排序边,枚举选择的最小的边. 1 #inclu ...