libcurl异步方式使用总结
来源:互联网 发布:从淘宝怎么买东西便宜 编辑:程序博客网 时间:2024/06/05 09:39
实习期间用到了libcurl来做HTTPS双向认证,用的是异步方式,简单总结一下。
libcurl这个库的同步方式很简单,不做介绍,而异步方式很难理解,本博客参考官网的demo讲解,刚开始看可能很蒙,最后会整合全流程。
使用步骤如下:
1.初始化创建一个multi句柄:
CURLM *multi = curl_multi_init();
2.对multi句柄设置socket回调和timer回调:
curl_multi_setopt(multi, CURLMOPT_SOCKETFUNCTION, multi_sock_cb);curl_multi_setopt(multi, CURLMOPT_SOCKETDATA, ¶m);curl_multi_setopt(multi, CURLMOPT_TIMERFUNCTION, multi_timer_cb);curl_multi_setopt(multi, CURLMOPT_TIMERDATA, ¶m);
3.对multi句柄添加easy句柄,异步开始:
CURL *easy = curl_easy_init();curl_easy_setopt(conn->easy, CURLOPT_URL, url);curl_easy_setopt(conn->easy, CURLOPT_WRITEFUNCTION, write_cb); // 负责读入数据的函数curl_easy_setopt(conn->easy, CURLOPT_WRITEDATA, &data);curl_multi_add_handle(multi, easy);
先看看第三行设置的write_cb
,该函数是你读入数据的函数:
/* * ptr 指向libcurl库读到的数据 * data 用户自定义的缓冲区, 上面第四行设置 */size_t write_cb(void *ptr, size_t size, size_t nmemb, void *data) { // 把ptr指向的数据拷到data}
在curl_multi_add_handle
运行结束的那一刻,第2步设置的multi_timer_cb
马上被拉起执行,让我们看看multi_timer_cb
的函数声明:
/* * multi 第一步创建的句柄 * timeout_ms libcurl库维护的一个超时时间,具体怎么算不清楚,回调时会自动赋值 * param 第二步设置的参数 * return 错误码 */int multi_timer_cb(CURLM *multi, long timeout_ms, void *param)
libcurl库本身没有定时器功能,只是告诉你一个定时时间timeout_ms
,这就要求我们自己维护一个定时器和到期的回调函数timer_cb
。
伪代码表示如下:
int multi_timer_cb(CURLM *multi, long timeout_ms, void *param) { timer_.add(timer_cb, ms); // ms后执行timer_cb}
timer_cb
主要调用libcurl的两个函数:
void timer_cb(param...) { CURLMcode rc; rc = curl_multi_socket_action(multi, CURL_SOCKET_TIMEOUT, 0, &still_running); while((msg = curl_multi_info_read(multi, &msgs_left))) { // 判断数据是否读完 if(msg->msg == CURLMSG_DONE) { // 清理资源操作 } }}
而multi_sock_cb
类似如此:
/* * e 第三步添加的easy句柄 * s libcurl创建维护的socket * what 执行动作(读或写) */int multi_sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp)
在libcurl维护的socket描述符发生状态改变时(变回可读或可写),multi_sock_cb
才会被回调。注意,函数回调时,第二个参数是socket描述符,这是libcurl维护创建的,但是你把它添加到poller(代指epoll或poll的封装类)或者libev等事件触发器中去,并设置回调函数,伪代码如下
int multi_sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp) { poller.add(s, socket_cb); // 当描述符可读和可写时,调用socket_cb}
看到这里是不是懵逼,不要急,最后会讲解全流程。socket_cb
里也是调用两个libcurl函数:
void socket_cb(param...) { CURLMcode rc; rc = curl_multi_socket_action(multi, CURL_SOCKET_TIMEOUT, 0, &still_running); while((msg = curl_multi_info_read(multi, &msgs_left))) { // 判断数据是否读完 if(msg->msg == CURLMSG_DONE) { // 清理资源操作 } }}
好了,函数写成这样就差不多了(都是伪代码,具体用法还是看demo)。那么这代码到底是怎么执行的呢,请看下图。
1、在curl_multi_add_handle
之后,multi_timer_cb
会马上被拉起调用,然后第一次调用的话timeout是0ms,所以timer_cb
也会被拉起,然后调用curl_multi_socket_action
。
2、此时,请注意在curl_multi_add_handle
之前已经设置过了url了,所以此时是需要发起http请求,即写请求,所以在curl_multi_socket_action
中libcurl会创建一个socket描述符,然后状态变为可写。
3、此时,因为libcurl的socket描述符状态发生改变,所以multi_sock_cb
会被拉起,multi_sock_cb
中就把socket描述符添加到poller中,设置写事件的回调函数为socket_cb
。
4、因为socket描述符是可写的,所以poller会调用sock_cb
,curl_multi_socket_action
又被调用,而此函数就会发送http请求(即libcurl负责写fd)。
5、等到http请求被发送完,就需要接收响应,所以libcurl会把socket描述符从写状态改为读状态。
6、因为socket描述符变为可读,状态改变,multi_sock_cb
又被调用,此时在poller中,将socket描述符的读事件回调函数设置为socket_cb
。
7、当响应到来的时候,socket描述符可读,调用socket_cb
,从而调用curl_multi_socket_action
,该函数就就会异步调用之前设置的、负责读入数据的write_cb
,从而读入数据。
8、 不断重复上一个步骤,直到数据被读完,此时libcurl会把socket描述符设置为删除状态,所以multi_sock_cb
会被回调,负责清理资源。而且,curl_multi_info_read
会判断已经读完数据,可以在这里进行数据转发,最终进行资源清理。注意,最终读到的数据,会在write_cb
设置的data
中(前提是你有在write_cb
中保存下来哈哈哈~)。
总结:
这库使用起来十分奇怪,我看了几天才看懂用法,我这篇博文写得十分简陋,最好的学习方法还是把demo跑一遍,看看打印出来的日志,还有详细的参数设置,需要去看官网文档。
- libcurl异步方式使用总结
- libcurl的使用总结
- libcurl的使用总结
- PHP libcurl使用总结
- 使用libcurl异步发送http请求
- 【转载】libcurl的使用总结
- static libcurl 在vs中使用方式
- libcurl的使用总结(一)
- libcurl的使用总结(二)
- libcurl基本使用 -- libcurl
- 使用libcurl第三方库实现Android异步任务
- 使用libcurl进行异步并发访问与文件上传
- libcurl使用
- libcurl使用
- 使用libcurl
- libcurl使用
- libcurl 使用
- libcurl使用
- Android TabLayout的简易使用
- [Archlinux] Elelctronic Wechat安装
- 1036. 跟奥巴马一起编程(15)
- gulpfile.js常用配置文件
- [OPENCV]使用Adaptive Boosting进行分类
- libcurl异步方式使用总结
- Maven下使用jetty进行debug
- Linux虚拟机(ubuntu16)网络设置方法
- 数据增强
- pat 乙级 1072. 开学寄语(20)
- 微信公众号支付 (一、获取openId)
- 第三周——项目一—顺序表的基本运算
- easyui edatagrid 在编辑状态下表头与表单错位的解决方法
- 虚拟机栈和本地方法栈溢出