libcurl实现解析(3) - libcurl对select的使用
1.前言
在本系列的前一篇文章中。介绍了libcurl对poll()的使用。
參考"libcurl原理解析(2) - libcurl对poll的使用"。
本篇文章主要分析curl_poll()中对select()的封装使用。与前一篇类似,我们仅仅分离出与select相关的代码。
2.curl_poll函数分析
这个函数中使用到的一些其他的数据结构,能够參考前一篇文章中的介绍。本篇不再介绍。
/*
这个函数是对poll()的封装。假设poll()不存在,则使用select()替代。
假设使用的是select(),而且文件描写叙述符fd太大,超过了FD_SETSIZE,则返回error。
假设传入的timeout值是一个负数。则会无限的等待。直到没有有效的fd被提供。 当发生
这样的情况(没有有效的fd)时。则负数timeout值会被忽略,且函数会马上超时。 返回值:
-1 = 系统调用错误或fd>=FD_SETSIZE.
0 = timeout.
N = 返回的pollfd结构体的个数,且当中的revents成员不为0.
*/
int Curl_poll(struct pollfd ufds[], unsigned int nfds, int timeout_ms)
{
struct timeval pending_tv;
struct timeval *ptimeout;
fd_set fds_read;
fd_set fds_write;
fd_set fds_err;
curl_socket_t maxfd; struct timeval initial_tv = { 0, 0 };
bool fds_none = TRUE; //用于验证传入的ufds数组是否有效
unsigned int i;
int pending_ms = 0;
int error; //保存错误码
int r; //检測全部fd中是否存在有效的fd。
//假设至少存在一个有效的fd,则fds_none置为false。停止检測
if (ufds)
{
for (i = 0; i < nfds; i++)
{
if (ufds[i].fd != CURL_SOCKET_BAD)
{
fds_none = FALSE;
break;
}
}
} //假设全部的fd都是无效的(即bad socket, -1)。则等待一段时间后。直接返回。
if (fds_none)
{
r = Curl_wait_ms(timeout_ms); //此函数会随后进行分析
return r;
} //当传入的timeout值是一个负数(堵塞情形)或者0时。则无需衡量elapsed time.
//否则,获取当前时间。 if (timeout_ms > 0)
{
pending_ms = timeout_ms;
initial_tv = curlx_tvnow();//调用gettimeofday()或time()获取当前时间
} //每次调用select()前都须要又一次初始化fdset,由于它们既是输入參数又是输出參数。
FD_ZERO(&fds_read);
FD_ZERO(&fds_write);
FD_ZERO(&fds_err);
maxfd = (curl_socket_t)-1; for (i = 0; i < nfds; i++)
{
ufds[i].revents = 0;
if (ufds[i].fd == CURL_SOCKET_BAD) //跳过无效的fd
continue;
VERIFY_SOCK(ufds[i].fd); //检測是否0<=fd<FD_SETSIZE.超出这个范围。则返回-1. if (ufds[i].events & (POLLIN | POLLOUT | POLLPRI |
POLLRDNORM | POLLWRNORM | POLLRDBAND))
{
if (ufds[i].fd > maxfd) //获取到最大的fd,做为select()的第一个參数。
maxfd = ufds[i].fd;
if (ufds[i].events & (POLLRDNORM | POLLIN))
FD_SET(ufds[i].fd, &fds_read);
if (ufds[i].events & (POLLWRNORM | POLLOUT))
FD_SET(ufds[i].fd, &fds_write);
if (ufds[i].events & (POLLRDBAND | POLLPRI))
FD_SET(ufds[i].fd, &fds_err);
}
} //做为select()的timeout參数
ptimeout = (timeout_ms < 0) ? NULL : &pending_tv; do
{
if (timeout_ms > 0)
{
pending_tv.tv_sec = pending_ms / 1000;
pending_tv.tv_usec = (pending_ms % 1000) * 1000;
}
else if (!timeout_ms)
{
pending_tv.tv_sec = 0;
pending_tv.tv_usec = 0;
} //真正调用select(). 第2。3,4參数已经在前面初始化(清空)过了。
r = select((int)maxfd + 1, &fds_read, &fds_write, &fds_err, ptimeout);
if (r != -1) //select调用成功,结束循环
break; //select调用失败。返回-1。通过errno能够获取到错误码。 error = SOCKERRNO; //宏定义。 #define SOCKERRNO (errno) //以下的error_not_EINTR 是宏定义.
//#define error_not_EINTR (0 || error != EINTR)
if (error && error_not_EINTR) //检測是否存在error,且不是EINTR错误
break; //没有出错或者存在EINTR错误。则推断是否继续运行select()
if (timeout_ms > 0)
{
//elapsed_ms是宏定义。
//#define elapsed_ms (int)curlx_tvdiff(curlx_tvnow(), initial_tv)
pending_ms = timeout_ms - elapsed_ms;
if (pending_ms <= 0)
{
r = 0; //模拟select超时的情形
break;
}
}
} while (r == -1);
/*如今能够对上面的这个while循环总结一下:
1.假设select调用成功(即返回值>=0),则结束循环
2.假设select调用失败(即返回值==-1),则检測errno是否为EINTR错误。
假设不是EINTR,则结束循环。
假设是EINTR,则检測是否已经超时。超时则结束循环,没有超时则继续select()。
*/ if (r < 0) //select()调用失败
return -1;
if (r == 0) //select()超时
return 0; //select()调用成功, 统计当中状态发生改变的fd的个数,保存至r.
r = 0;
for (i = 0; i < nfds; i++)
{
ufds[i].revents = 0;
if (ufds[i].fd == CURL_SOCKET_BAD)
continue;
if (FD_ISSET(ufds[i].fd, &fds_read)) //fd可读
ufds[i].revents |= POLLIN;
if (FD_ISSET(ufds[i].fd, &fds_write)) //fd可写
ufds[i].revents |= POLLOUT;
if (FD_ISSET(ufds[i].fd, &fds_err)) //fd出错
ufds[i].revents |= POLLPRI;
if (ufds[i].revents != 0)
r++;
} return r;
}
这个函数运行完毕后。第一个输入參数ufds中的成员revents可能会被改动。最后。函数返回select()的实际返回值。
3.curl_wait_ms函数分析
以下是curl_wait_ms()函数的详细实现。
也是基于poll或者select来实现的。这里也仅仅讨论它的select()实现版本号。
/*
这个函数用于等待特定的时间值。在函数Curl_socket_ready()以及Curl_poll()中被调用。
当没有提供不论什么fd来检測时。则仅仅是等待特定的一段时间。 假设是在windows平台下,则winsock中的poll()以及select()超时机制,须要一个有效的socket fd.
这个函数不同意无限等待,假设传入的值是0或者负数。则马上返回。
超时时间的精度以及最大值。取决于系统。 返回值:
-1 = 系统调用错误,或无效的输入值(timeout),或被中断。
0 = 指定的时间已经超时
*/
int Curl_wait_ms(int timeout_ms)
{
struct timeval pending_tv;
struct timeval initial_tv;
int pending_ms;
int error;
int r = 0; if (!timeout_ms) //超时值为0,马上返回
return 0;
if (timeout_ms < 0) //不能为负数
{
SET_SOCKERRNO(EINVAL);
return -1;
} pending_ms = timeout_ms;
initial_tv = curlx_tvnow();
do
{
pending_tv.tv_sec = pending_ms / 1000;
pending_tv.tv_usec = (pending_ms % 1000) * 1000; r = select(0, NULL, NULL, NULL, &pending_tv); if (r != -1) //select()调用成功,则跳出循环
break; //select调用失败。返回-1。通过errno能够获取到错误码。
error = SOCKERRNO; //宏定义。 #define SOCKERRNO (errno) //以下的error_not_EINTR 是宏定义. #define error_not_EINTR (0 || error != EINTR)
if (error && error_not_EINTR) ////检測是否存在error,且不是EINTR错误
break; //elapsed_ms是宏定义:
//#define elapsed_ms (int)curlx_tvdiff(curlx_tvnow(), initial_tv)
pending_ms = timeout_ms - elapsed_ms;
if (pending_ms <= 0)
{
r = 0; //模拟select超时的情形
break;
}
} while (r == -1); //确保返回值r仅仅能为-1(超时失败)或者0(超时成功)。 //r不可能大于0,由于传入到select()函数的3个fdset数组所有都是NULL。假设select的返回值>0,则说明调用出问题了。
//故这里会将r置为-1,即调用超时失败。
if (r)
r = -1;
return r;
}
libcurl实现解析(3) - libcurl对select的使用的更多相关文章
- jquery动态刷新select的值,后台传过来List<T>,前台解析后填充到select的option中
jquery动态刷新select的值:将后台传来的List<T>赋值到select下的option. 第一个select选择后出发该方法refreshMerchant(params),传递 ...
- Mybaits 源码解析 (七)----- Select 语句的执行过程分析(下篇)(Mapper方法是如何调用到XML中的SQL的?)全网最详细,没有之一
我们上篇文章讲到了查询方法里面的doQuery方法,这里面就是调用JDBC的API了,其中的逻辑比较复杂,我们这边文章来讲,先看看我们上篇文章分析的地方 SimpleExecutor public & ...
- linux c libcurl的简单使用(转)
curl是Linux下一个非常著名的下载库,通过这个库,可以很简单的实现文件的下载等操作.看一个简单的例子: #include <curl/curl.h> #include <std ...
- Libcurl细说
libcurl教程 原文地址:http://curl.haxx.se/libcurl/c/libcurl-tutorial.html 译者:JGood(http://blog.csdn.net/J ...
- libcurl教程
名称 libcurl 的编程教程 目标 本文档介绍使用libcurl编程的一般原则和一些基本方法.本文主要是介绍 c 语言的调用接口,同时也可能很好的适用于其他类 c 语言的接口. 跨平台的可移植代码 ...
- C++ 用libcurl库进行http通讯网络编程
使用libcurl完成http通讯,很方便而且是线程安全,转载一篇比较好的入门文章 转载自 http://www.cnblogs.com/moodlxs/archive/2012/10/15/2724 ...
- C++ 用libcurl库进行http通讯网络编程(转)
转载:http://www.cnblogs.com/moodlxs/archive/2012/10/15/2724318.html 目录索引: 一.LibCurl基本编程框架 二.一些基本的函数 三. ...
- cocos2dx libcurl
转自:http://www.himigame.com/curl-libcurl/878.html 本篇介绍使用libcurl编程的一般原则和一些基本方法.本文主要是介绍 c 语言的调用接口,同时也可能 ...
- libcurl
一.LibCurl基本编程框架 二.一些基本的函数 三.curl_easy_setopt函数部分选项介绍 四.curl_easy_perform 函数说明(error 状态码) 五.libcurl使用 ...
随机推荐
- 12 C#中的方法
还记得我们的第一个程序吗?忘记了?那你要努力了.我们的第一个程序是就是往dos窗口输出一些字符串.在哪个程序中只有一个方法,Main方法.Main方法是一个特殊的方法,但是它也是一个方法.为什么说Ma ...
- 8.Layers Editor
图层编辑 Ventuz5中有两种类型的场景,分别是2D图层和3D图层.3D图层包含Content和Hierarchy,而2D图层只包含Content.默认情况下,图层编辑器显示在Ventuz中的左上角 ...
- drupal-使用hook_preprocess_field在paragraph的accordion中添加自定义数据
描述:我的accordion类型原先只有两个字段,分别是title和content.显示在页面上会默认隐藏其内容,点击“+”会显示内容.然而现在有一个新需求,就是加一个开关使编辑内容者可以选择默认“展 ...
- [Windows Server 2012] Filezilla安全加固方法
★ 欢迎来到[护卫神·V课堂],网站地址:http://v.huweishen.com ★ 护卫神·V课堂 是护卫神旗下专业提供服务器教学视频的网站,每周更新视频. ★ 本节我们将带领大家:FileZ ...
- Cuder - 用C++11封装的CUDA类
以前写cuda:初始化环境,申请显存,初始化显存,launch kernel,拷贝数据,释放显存.一个页面大部分都是这些繁杂但又必须的操作,有时还会忘掉释放部分显存. 今天用C++11封装了这些CUD ...
- C# call Win32 api时,-1如何转换为DWORD
当使用(uint)-1时,编译器会给出警告:常量-1无法转换为uint,使用unchecked语句重写.DWORD在转换为C#类型时为uint,既然无法使用uint强制转型(-1),那就需要其他办法了 ...
- 错误处理:vmware下克隆centos7配置静态ip地址网卡问题
vmware下克隆centos7,在配置静态ip地址,重启网卡存在问题,还是mac地址问题 ip addr show 查看下mac地址,配置文件修改下,重启网卡正常了
- R包
查看默认安装包的位置 .libPaths() 移除包 remove.packages("package_name") 查看所有安装的包 library() 按 q 退出包列表 ...
- php base64互转pdf
/* * base64转pdf */ function base642pdf($formTxt,$toPdf) { $file = file_get_contents($formTxt);//读 $d ...
- 跳转语句(break、continue)
break语句 在switch条件语句和循环语句中都可以使用break语句.当它出现在switch条件语句中时,作用是终止某个case并跳出switch结构. 当它出现在循环语句中,作用是跳出循环语句 ...