axel_do主体部分,尝试从多个连接select方式去读取数据,如果读取失败或者连接超时就重新连接。

下面是代码分析.

  1. //下载的主循环
  2. void axel_do( axel_t *axel )
  3. {
  4. fd_set fds[1];
  5. int hifd, i;
  6. long long int remaining,size;
  7. struct timeval timeval[1];
  8. /* Create statefile if necessary                                */
  9. //如果到了保存状态的时间,保存当前状态到状态文件
  10. if( gettime() > axel->next_state )
  11. {
  12. //把连接的个数,当前下载量,每个连接当前的下载进度保存起来,
  13. //如果状态文件已经存在,就清空重写,目的是只保存一份
  14. //这样的话,如果程序异常退出,下次开启任务时,就能从状态文件中
  15. //重新加载最近的下载状态(不是100%准确的状态,因为保存状态是有周期的),
  16. //接着下载
  17. save_state( axel );
  18. //计算下一次状态保存时间
  19. axel->next_state = gettime() + axel->conf->save_state_interval;
  20. }
  21. //采用多线程做连接,但是还是单线程select做传输
  22. /* Wait for data on (one of) the connections                        */
  23. FD_ZERO( fds );
  24. hifd = 0;
  25. //经典的select方式,把有用的socket描述符加入到集合中
  26. //而且,限于文件下载的特殊任务方式,基本上每个建立好的连接
  27. //都不会太空,有恒定持续的数据传输,因此在这里select的效率并不弱于epoll
  28. for( i = 0; i < axel->conf->num_connections; i ++ )
  29. {
  30. if( axel->conn[i].enabled )
  31. FD_SET( axel->conn[i].fd, fds );
  32. hifd = max( hifd, axel->conn[i].fd );
  33. }
  34. //没有任何连接,等待重试
  35. if( hifd == 0 )
  36. {
  37. /* No connections yet. Wait...                                */
  38. usleep( 100000 );
  39. goto conn_check;
  40. }
  41. else
  42. {
  43. //超时0.1秒
  44. timeval->tv_sec = 0;
  45. timeval->tv_usec = 100000;
  46. /* A select() error probably means it was interrupted
  47. by a signal, or that something else's very wrong...        */
  48. //select等待数据到来
  49. if( select( hifd + 1, fds, NULL, NULL, timeval ) == -1 )
  50. {
  51. //值位ready为-1,这样,下载的主循环将退出,下载失败
  52. axel->ready = -1;
  53. return;
  54. }
  55. }
  56. /* Handle connections which need attention                        */
  57. //循环读取每个socket的数据
  58. for( i = 0; i < axel->conf->num_connections; i ++ )
  59. if( axel->conn[i].enabled ) {
  60. //检测,如果该socket有数据到来,就读取
  61. if( FD_ISSET( axel->conn[i].fd, fds ) )
  62. {
  63. //更新最后一次读取数据的时间
  64. axel->conn[i].last_transfer = gettime();
  65. //尝试读取数据
  66. size = read( axel->conn[i].fd, buffer, axel->conf->buffer_size );
  67. //读取失败
  68. if( size == -1 )
  69. {
  70. if( axel->conf->verbose )
  71. {
  72. axel_message( axel, _("Error on connection %i! "
  73. "Connection closed"), i );
  74. }
  75. //关闭当前连接,并不等于放弃,还可能重新连接。。
  76. axel->conn[i].enabled = 0;
  77. conn_disconnect( &axel->conn[i] );
  78. continue;
  79. }//当前连接的数据读取结束
  80. else if( size == 0 )
  81. {
  82. if( axel->conf->verbose )
  83. {
  84. /* Only abnormal behaviour if:                */
  85. if( axel->conn[i].currentbyte < axel->conn[i].lastbyte && axel->size != INT_MAX )
  86. {
  87. axel_message( axel, _("Connection %i unexpectedly closed"), i );
  88. }
  89. else
  90. {
  91. axel_message( axel, _("Connection %i finished"), i );
  92. }
  93. }
  94. //如果是不支持并发分片下载(也就是说是单连接下载),表明下载完成
  95. if( !axel->conn[0].supported )
  96. {
  97. axel->ready = 1;
  98. }
  99. axel->conn[i].enabled = 0;
  100. conn_disconnect( &axel->conn[i] );
  101. continue;
  102. }
  103. /* remaining == Bytes to go                                        */
  104. remaining = axel->conn[i].lastbyte - axel->conn[i].currentbyte + 1;
  105. //需要填充的小于读取的,下载完成
  106. if( remaining < size )
  107. {
  108. if( axel->conf->verbose )
  109. {
  110. axel_message( axel, _("Connection %i finished"), i );
  111. }
  112. axel->conn[i].enabled = 0;
  113. //关闭连接
  114. conn_disconnect( &axel->conn[i] );
  115. //修改需要的数据量大小,比如需要20字节,下载了30字节,那么就只要20字节
  116. size = remaining;
  117. /* Don't terminate, still stuff to write!        */
  118. }
  119. /* This should always succeed..                                */
  120. //调整偏移,写文件
  121. lseek( axel->outfd, axel->conn[i].currentbyte, SEEK_SET );
  122. if( write( axel->outfd, buffer, size ) != size )
  123. {
  124. //写失败,退出
  125. axel_message( axel, _("Write error!") );
  126. axel->ready = -1;
  127. return;
  128. }
  129. //修改偏移
  130. axel->conn[i].currentbyte += size;
  131. axel->bytes_done += size;
  132. }
  133. else //当前socket描述符不在select中,检查超时
  134. {
  135. //传输超时,关闭连接
  136. if( gettime() > axel->conn[i].last_transfer + axel->conf->connection_timeout )
  137. {
  138. if( axel->conf->verbose )
  139. axel_message( axel, _("Connection %i timed out"), i );
  140. conn_disconnect( &axel->conn[i] );
  141. axel->conn[i].enabled = 0;
  142. }
  143. } }
  144. //如果一切ok,就返回
  145. if( axel->ready )
  146. return;
  147. conn_check:
  148. /* Look for aborted connections and attempt to restart them.        */
  149. //检查有问题的连接,如果未下载完,并且出错了,重新启动线程,开始传输
  150. for( i = 0; i < axel->conf->num_connections; i ++ )
  151. {
  152. //连接无效并且未下载完
  153. if( !axel->conn[i].enabled && axel->conn[i].currentbyte < axel->conn[i].lastbyte )
  154. {
  155. //状态为0,表明setup_thread是成功执行了的,并且已经执行问,因此,连接的初始化没问题,调用join回收
  156. if( axel->conn[i].state == 0 )
  157. {
  158. // Wait for termination of this thread
  159. pthread_join(*(axel->conn[i].setup_thread), NULL);
  160. conn_set( &axel->conn[i], axel->url->text );
  161. axel->url = axel->url->next;
  162. /* axel->conn[i].local_if = axel->conf->interfaces->text;
  163. axel->conf->interfaces = axel->conf->interfaces->next; */
  164. if( axel->conf->verbose >= 2 )
  165. axel_message( axel, _("Connection %i downloading from %s:%i using interface %s"),
  166. i, axel->conn[i].host, axel->conn[i].port, axel->conn[i].local_if );
  167. //状态设置为1,表示setup_thread开始执行,设置为0,表示setup_thread线程执行结束
  168. axel->conn[i].state = 1;
  169. if( pthread_create( axel->conn[i].setup_thread, NULL, setup_thread, &axel->conn[i] ) == 0 )
  170. {
  171. axel->conn[i].last_transfer = gettime();
  172. }
  173. else
  174. {
  175. axel_message( axel, _("pthread error!!!") );
  176. axel->ready = -1;
  177. }
  178. }
  179. else //setup_thread线程还未执行完,也就是说连接建立过程还未完成,需要检查连接超时
  180. {
  181. //超时了,就取消她...
  182. if( gettime() > axel->conn[i].last_transfer + axel->conf->reconnect_delay )
  183. {
  184. pthread_cancel( *axel->conn[i].setup_thread );
  185. axel->conn[i].state = 0;
  186. }
  187. }
  188. }
  189. }
  190. /* Calculate current average speed and finish_time                */
  191. //计算平均速度
  192. axel->bytes_per_second = (int) ( (double) ( axel->bytes_done - axel->start_byte ) / ( gettime() - axel->start_time ) );
  193. //估算结束时间
  194. axel->finish_time = (int) ( axel->start_time + (double) ( axel->size - axel->start_byte ) / axel->bytes_per_second );
  195. /* Check speed. If too high, delay for some time to slow things
  196. down a bit. I think a 5% deviation should be acceptable.        */
  197. //速度调整
  198. if( axel->conf->max_speed > 0 )
  199. {
  200. //如果超速了
  201. if( (float) axel->bytes_per_second / axel->conf->max_speed > 1.05 )
  202. axel->delay_time += 10000;
  203. //速度太低,少休息会儿
  204. else if( ( (float) axel->bytes_per_second / axel->conf->max_speed < 0.95 ) && ( axel->delay_time >= 10000 ) )
  205. axel->delay_time -= 10000;
  206. //速度太低,干脆不休息
  207. else if( ( (float) axel->bytes_per_second / axel->conf->max_speed < 0.95 ) )
  208. axel->delay_time = 0;
  209. usleep( axel->delay_time );
  210. }
  211. /* Ready?                                                        */
  212. //下载完了?
  213. if( axel->bytes_done == axel->size )
  214. axel->ready = 1;
  215. }

转载于:https://www.cnblogs.com/shepherd2012/archive/2012/08/06/2625670.html

Axel之 -axel_do剖析相关推荐

  1. volatile关键字之全面深度剖析

    引言 volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在Java 5之后,volatile关键字 ...

  2. TensorFlow基础剖析

    TensorFlow基础剖析 一.概述 TensorFlow 是一个使用数据流图 (Dataflow Graph) 表达数值计算的开源软件库.它使用节点表示抽象的数学计算,并使用 OP 表达计算的逻辑 ...

  3. c语言赋值x为字母,C语言算术、赋值、关系、逻辑运算详细剖析---

    标识符和关键字 ¨标识符:用来标识程序中的变量.符号常量.函数.数组.类型.文件等对象的名字.标识符只能由字母.数字和下划线组成,且第一个字符必需为字母或下划线.C语言中大小写字母是两个不同的字符. ...

  4. 如何在HHDI中进行数据质量探查并获取数据剖析报告

    通过执行多种数据剖析规则,对目标表(或一段SQL语句)进行数据质量探查,从而得到其数据质量情况.目前支持以下几种数据剖析类型,分别是:数字值分析.值匹配检查.字符值分析.日期值分析.布尔值分析.重复值 ...

  5. [Mac] mac linux 多线程下载利器 axel

    ​> 之前做过一些文件下载的统计,发现谷歌浏览器chrome和火狐firefox, 一般都是单线程的下载文件,360浏览器却是多线程的下载. 如今切换到了mac上,发现没有360哪个浏览器,就像 ...

  6. 老李推荐:第14章4节《MonkeyRunner源码剖析》 HierarchyViewer实现原理-装备ViewServer-端口转发 1...

    老李推荐:第14章4节<MonkeyRunner源码剖析> HierarchyViewer实现原理-装备ViewServer-端口转发 在初始化HierarchyViewer的实例过程中, ...

  7. JS魔法堂:mmDeferred源码剖析

    一.前言 avalon.js的影响力愈发强劲,而作为子模块之一的mmDeferred必然成为异步调用模式学习之旅的又一站呢!本文将记录我对mmDeferred的认识,若有纰漏请各位指正,谢谢.项目请见 ...

  8. Linux 文件系统剖析

    Linux 文件系统剖析 按照分层结构讨论 Linux 文件系统 M. Tim Jones, 顾问工程师, Emulex Corp. 简介: 在文件系统方面,Linux® 可以算得上操作系统中的 &q ...

  9. 剖析PHP中的输出缓冲

    剖析PHP中的输出缓冲 本文按署名·非商业用途·保持一致授权 作者:  ,发表于2005年12月24日01时54分 我们先来看一段代码. <?php for ($i=10; $i>0; $ ...

最新文章

  1. insight切换窗口 source_Source Insight函数调用关系显示设置
  2. Sql Server 2012 分页方法分析(offset and fetch)
  3. 09-LearnTheArchitecture-MemoryManagement
  4. 【效率】推荐一款Markdown神器 ! ! !
  5. CSDN《某一程序员竟然吃过shi?让我们走进他的生活,揭露背后的故事》
  6. php order by where,无合适where条件过滤时尽量选择order by后的字段以驱动表进行查询...
  7. 芝枝.计算机与人文科学,计算机与人文科学
  8. linux在生活中的应用例子,LINUX操作系统在教师学生日常生活中的应用(全).docx
  9. autojs 复制到粘贴板_JS复制到剪贴板示例代码
  10. 下载网页上的各种视频只需四步
  11. mysql酒店客房管理系统的设计_《酒店客房管理系统设计》总结
  12. 批处理net命令集合
  13. Mac与远程服务器数据交互软件Cyberduck
  14. html在抽奖图片自动效果图,jQuery实现图片随机切换、抽奖功能(实例代码)
  15. PowerJob 的自实现高可用方案,妙妙妙!
  16. could not locate named parameter [***]; nested exception is org.hibernate.QueryParameterException: c
  17. 职业生涯规划设计-分析第一部分
  18. 我的世界java版骷髅_我的世界:击杀凋灵骷髅,才会掉落“凋灵头”?听起来有些不靠谱...
  19. 阿里云上基于WordPress快速搭建个人博客
  20. Win10系统更新提示错误0xc1900403的解决方法

热门文章

  1. 2022-2028年中国超声波探伤仪行业市场现状调研及发展前景分析报告
  2. 2022-2028年中国废旧塑料回收产业研究及前瞻分析报告
  3. 通俗理解tf.name_scope()、tf.variable_scope()
  4. LeetCode简单题之数组序号转换
  5. 谷粒商城学习笔记——第一期:项目简介
  6. NVIDIA 自动驾驶软件平台
  7. TensorRT PoolingLayer
  8. 利用NVIDIA NGC的TensorRT容器优化和加速人工智能推理
  9. ADAS系统长篇综述(下)
  10. [JS][C++]两题斐波那契数列:上台阶、triangle