1.前言

在本系列的前一篇文章中。介绍了libcurl对poll()的使用。

參考"libcurl原理解析(2) - libcurl对poll的使用"。

本篇文章主要分析curl_poll()中对select()的封装使用。与前一篇类似,我们仅仅分离出与select相关的代码。

2.curl_poll函数分析

这个函数中使用到的一些其他的数据结构,能够參考前一篇文章中的介绍。本篇不再介绍。

  1. /*
  2. 这个函数是对poll()的封装。假设poll()不存在,则使用select()替代。
  3. 假设使用的是select(),而且文件描写叙述符fd太大,超过了FD_SETSIZE,则返回error。
  4. 假设传入的timeout值是一个负数。则会无限的等待。直到没有有效的fd被提供。
  5.  
  6. 当发生
  7. 这样的情况(没有有效的fd)时。则负数timeout值会被忽略,且函数会马上超时。
  8.  
  9. 返回值:
  10. -1 = 系统调用错误或fd>=FD_SETSIZE.
  11. 0 = timeout.
  12. N = 返回的pollfd结构体的个数,且当中的revents成员不为0.
  13. */
  14. int Curl_poll(struct pollfd ufds[], unsigned int nfds, int timeout_ms)
  15. {
  16. struct timeval pending_tv;
  17. struct timeval *ptimeout;
  18. fd_set fds_read;
  19. fd_set fds_write;
  20. fd_set fds_err;
  21. curl_socket_t maxfd;
  22.  
  23. struct timeval initial_tv = { 0, 0 };
  24. bool fds_none = TRUE; //用于验证传入的ufds数组是否有效
  25. unsigned int i;
  26. int pending_ms = 0;
  27. int error; //保存错误码
  28. int r;
  29.  
  30. //检測全部fd中是否存在有效的fd。
  31. //假设至少存在一个有效的fd,则fds_none置为false。停止检測
  32. if (ufds)
  33. {
  34. for (i = 0; i < nfds; i++)
  35. {
  36. if (ufds[i].fd != CURL_SOCKET_BAD)
  37. {
  38. fds_none = FALSE;
  39. break;
  40. }
  41. }
  42. }
  43.  
  44. //假设全部的fd都是无效的(即bad socket, -1)。则等待一段时间后。直接返回。
  45. if (fds_none)
  46. {
  47. r = Curl_wait_ms(timeout_ms); //此函数会随后进行分析
  48. return r;
  49. }
  50.  
  51. //当传入的timeout值是一个负数(堵塞情形)或者0时。则无需衡量elapsed time.
  52. //否则,获取当前时间。
  53.  
  54. if (timeout_ms > 0)
  55. {
  56. pending_ms = timeout_ms;
  57. initial_tv = curlx_tvnow();//调用gettimeofday()或time()获取当前时间
  58. }
  59.  
  60. //每次调用select()前都须要又一次初始化fdset,由于它们既是输入參数又是输出參数。
  61. FD_ZERO(&fds_read);
  62. FD_ZERO(&fds_write);
  63. FD_ZERO(&fds_err);
  64. maxfd = (curl_socket_t)-1;
  65.  
  66. for (i = 0; i < nfds; i++)
  67. {
  68. ufds[i].revents = 0;
  69. if (ufds[i].fd == CURL_SOCKET_BAD) //跳过无效的fd
  70. continue;
  71. VERIFY_SOCK(ufds[i].fd); //检測是否0<=fd<FD_SETSIZE.超出这个范围。则返回-1.
  72.  
  73. if (ufds[i].events & (POLLIN | POLLOUT | POLLPRI |
  74. POLLRDNORM | POLLWRNORM | POLLRDBAND))
  75. {
  76. if (ufds[i].fd > maxfd) //获取到最大的fd,做为select()的第一个參数。
  77. maxfd = ufds[i].fd;
  78. if (ufds[i].events & (POLLRDNORM | POLLIN))
  79. FD_SET(ufds[i].fd, &fds_read);
  80. if (ufds[i].events & (POLLWRNORM | POLLOUT))
  81. FD_SET(ufds[i].fd, &fds_write);
  82. if (ufds[i].events & (POLLRDBAND | POLLPRI))
  83. FD_SET(ufds[i].fd, &fds_err);
  84. }
  85. }
  86.  
  87. //做为select()的timeout參数
  88. ptimeout = (timeout_ms < 0) ? NULL : &pending_tv;
  89.  
  90. do
  91. {
  92. if (timeout_ms > 0)
  93. {
  94. pending_tv.tv_sec = pending_ms / 1000;
  95. pending_tv.tv_usec = (pending_ms % 1000) * 1000;
  96. }
  97. else if (!timeout_ms)
  98. {
  99. pending_tv.tv_sec = 0;
  100. pending_tv.tv_usec = 0;
  101. }
  102.  
  103. //真正调用select(). 第2。3,4參数已经在前面初始化(清空)过了。
  104. r = select((int)maxfd + 1, &fds_read, &fds_write, &fds_err, ptimeout);
  105. if (r != -1) //select调用成功,结束循环
  106. break;
  107.  
  108. //select调用失败。返回-1。通过errno能够获取到错误码。
  109.  
  110. error = SOCKERRNO; //宏定义。
  111.  
  112. #define SOCKERRNO (errno)
  113.  
  114. //以下的error_not_EINTR 是宏定义.
  115. //#define error_not_EINTR (0 || error != EINTR)
  116. if (error && error_not_EINTR) //检測是否存在error,且不是EINTR错误
  117. break;
  118.  
  119. //没有出错或者存在EINTR错误。则推断是否继续运行select()
  120. if (timeout_ms > 0)
  121. {
  122. //elapsed_ms是宏定义。
  123. //#define elapsed_ms (int)curlx_tvdiff(curlx_tvnow(), initial_tv)
  124. pending_ms = timeout_ms - elapsed_ms;
  125. if (pending_ms <= 0)
  126. {
  127. r = 0; //模拟select超时的情形
  128. break;
  129. }
  130. }
  131. } while (r == -1);
  132. /*如今能够对上面的这个while循环总结一下:
  133. 1.假设select调用成功(即返回值>=0),则结束循环
  134. 2.假设select调用失败(即返回值==-1),则检測errno是否为EINTR错误。
  135. 假设不是EINTR,则结束循环。
  136. 假设是EINTR,则检測是否已经超时。超时则结束循环,没有超时则继续select()。
  137. */
  138.  
  139. if (r < 0) //select()调用失败
  140. return -1;
  141. if (r == 0) //select()超时
  142. return 0;
  143.  
  144. //select()调用成功, 统计当中状态发生改变的fd的个数,保存至r.
  145. r = 0;
  146. for (i = 0; i < nfds; i++)
  147. {
  148. ufds[i].revents = 0;
  149. if (ufds[i].fd == CURL_SOCKET_BAD)
  150. continue;
  151. if (FD_ISSET(ufds[i].fd, &fds_read)) //fd可读
  152. ufds[i].revents |= POLLIN;
  153. if (FD_ISSET(ufds[i].fd, &fds_write)) //fd可写
  154. ufds[i].revents |= POLLOUT;
  155. if (FD_ISSET(ufds[i].fd, &fds_err)) //fd出错
  156. ufds[i].revents |= POLLPRI;
  157. if (ufds[i].revents != 0)
  158. r++;
  159. }
  160.  
  161. return r;
  162. }

这个函数运行完毕后。第一个输入參数ufds中的成员revents可能会被改动。最后。函数返回select()的实际返回值。

3.curl_wait_ms函数分析

以下是curl_wait_ms()函数的详细实现。

也是基于poll或者select来实现的。这里也仅仅讨论它的select()实现版本号。

  1. /*
  2. 这个函数用于等待特定的时间值。在函数Curl_socket_ready()以及Curl_poll()中被调用。
  3. 当没有提供不论什么fd来检測时。则仅仅是等待特定的一段时间。
  4.  
  5. 假设是在windows平台下,则winsock中的poll()以及select()超时机制,须要一个有效的socket fd.
  6. 这个函数不同意无限等待,假设传入的值是0或者负数。则马上返回。
  7. 超时时间的精度以及最大值。取决于系统。
  8.  
  9. 返回值:
  10. -1 = 系统调用错误,或无效的输入值(timeout),或被中断。
  11. 0 = 指定的时间已经超时
  12. */
  13. int Curl_wait_ms(int timeout_ms)
  14. {
  15. struct timeval pending_tv;
  16. struct timeval initial_tv;
  17. int pending_ms;
  18. int error;
  19. int r = 0;
  20.  
  21. if (!timeout_ms) //超时值为0,马上返回
  22. return 0;
  23. if (timeout_ms < 0) //不能为负数
  24. {
  25. SET_SOCKERRNO(EINVAL);
  26. return -1;
  27. }
  28.  
  29. pending_ms = timeout_ms;
  30. initial_tv = curlx_tvnow();
  31. do
  32. {
  33. pending_tv.tv_sec = pending_ms / 1000;
  34. pending_tv.tv_usec = (pending_ms % 1000) * 1000;
  35.  
  36. r = select(0, NULL, NULL, NULL, &pending_tv);
  37.  
  38. if (r != -1) //select()调用成功,则跳出循环
  39. break;
  40.  
  41. //select调用失败。返回-1。通过errno能够获取到错误码。
  42. error = SOCKERRNO; //宏定义。
  43.  
  44. #define SOCKERRNO (errno)
  45.  
  46. //以下的error_not_EINTR 是宏定义. #define error_not_EINTR (0 || error != EINTR)
  47. if (error && error_not_EINTR) ////检測是否存在error,且不是EINTR错误
  48. break;
  49.  
  50. //elapsed_ms是宏定义:
  51. //#define elapsed_ms (int)curlx_tvdiff(curlx_tvnow(), initial_tv)
  52. pending_ms = timeout_ms - elapsed_ms;
  53. if (pending_ms <= 0)
  54. {
  55. r = 0; //模拟select超时的情形
  56. break;
  57. }
  58. } while (r == -1);
  59.  
  60. //确保返回值r仅仅能为-1(超时失败)或者0(超时成功)。
  61.  
  62. //r不可能大于0,由于传入到select()函数的3个fdset数组所有都是NULL。假设select的返回值>0,则说明调用出问题了。
  63. //故这里会将r置为-1,即调用超时失败。
  64. if (r)
  65. r = -1;
  66. return r;
  67. }

libcurl实现解析(3) - libcurl对select的使用的更多相关文章

  1. jquery动态刷新select的值,后台传过来List<T>,前台解析后填充到select的option中

    jquery动态刷新select的值:将后台传来的List<T>赋值到select下的option. 第一个select选择后出发该方法refreshMerchant(params),传递 ...

  2. Mybaits 源码解析 (七)----- Select 语句的执行过程分析(下篇)(Mapper方法是如何调用到XML中的SQL的?)全网最详细,没有之一

    我们上篇文章讲到了查询方法里面的doQuery方法,这里面就是调用JDBC的API了,其中的逻辑比较复杂,我们这边文章来讲,先看看我们上篇文章分析的地方 SimpleExecutor public & ...

  3. linux c libcurl的简单使用(转)

    curl是Linux下一个非常著名的下载库,通过这个库,可以很简单的实现文件的下载等操作.看一个简单的例子: #include <curl/curl.h> #include <std ...

  4. Libcurl细说

    libcurl教程   原文地址:http://curl.haxx.se/libcurl/c/libcurl-tutorial.html 译者:JGood(http://blog.csdn.net/J ...

  5. libcurl教程

    名称 libcurl 的编程教程 目标 本文档介绍使用libcurl编程的一般原则和一些基本方法.本文主要是介绍 c 语言的调用接口,同时也可能很好的适用于其他类 c 语言的接口. 跨平台的可移植代码 ...

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

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

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

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

  8. cocos2dx libcurl

    转自:http://www.himigame.com/curl-libcurl/878.html 本篇介绍使用libcurl编程的一般原则和一些基本方法.本文主要是介绍 c 语言的调用接口,同时也可能 ...

  9. libcurl

    一.LibCurl基本编程框架 二.一些基本的函数 三.curl_easy_setopt函数部分选项介绍 四.curl_easy_perform 函数说明(error 状态码) 五.libcurl使用 ...

随机推荐

  1. Spring Cloud (4) 服务消费者-Feign

    Spring Cloud Feign Spring Cloud Feign 是一套基于Netflix Feign实现的声明式服务调用客户端.它使得编写Web服务客户端变得更加简单,我们只需要创建接口并 ...

  2. asp.net——根据时间,显示内容

    题目: 在VS 2010中建立一个网站,命名为Lab5_1,建立时注意项目文件夹的存放位置.根据当前时间,在页面上显示早上好或下午好或晚上好,并显示相应的不同图片. 体验: 一开始看到这个题目的时候, ...

  3. mysql中的各种concat

    引用:http://www.cnblogs.com/appleat/archive/2012/09/03/2669033.html 一.CONCAT()函数CONCAT()函数用于将多个字符串连接成一 ...

  4. js获取某年某月一共多少天

    const getDaysInMonth = (year, month) => { let date = new Date(year, month, 1); return new Date(da ...

  5. cannot load oci dll,193-navicate连接oracle的解决方法

    navicat连接远程数据库时 是因为instantclient是64位的,navicat是32位的,两者要一致 因为自己的navicat premium是破解版的,所以就另找了instantclie ...

  6. day05-控制流程之if/while/for

    目录 控制流程之if判断 控制流程之while循环 控制流程之for循环 控制流程之if判断 if 其实就是根据条件来做出不同的反应,如果这样就这样干,如果那样就那样干 1. 如果:成绩 > 9 ...

  7. Redis 之sentinel运维监控

    有三台redis服务器6379.6380.6381,配置6379为主服务器,6380与6381都为6379的从服务器.如果主服务器6379挂掉了,我们怎么办? 方式一:手动修改从服务器的配置,将638 ...

  8. std::vector遍历

    std::vector是我在标准库中实用最频繁的容器.总结一下在遍历和创建vector时需要注意的一些地方. 在不考虑线程安全问题的前提下,在C++11中有五种遍历方式. 方式一 for (size_ ...

  9. yum仓库配置ftpx协议

    [root@localhost ~]# iptables -F[root@localhost ~]# systemctl stop firewalld[root@localhost ~]# syste ...

  10. [如何在Mac下使用gulp] 1.创建项目及安装gulp

    1.创建项目 2.安装gulp 3.创建gulpfile.js文件 4.运行gulp 创建项目 -创建项目文件夹命名为firstGulp,并在firstGulp目录下运行 npm init .npm ...