CTorrent 程序源码分析
姚旭晨
目录
CTorrent 程序源码分析 ...................................................................................................................1
1. 前言 ............................................................................................................................................ 3
1.1 为什么要写这份文档......................................................................................................... 3
1.2 客户端的选择 ..................................................................................................................... 3
1.3 CTorrent 简介 .......................................................................................................................4
2. 准备工作 ....................................................................................................................................... 5
2.1 知识储备 ............................................................................................................................. 5
2.2 我对本篇源码分析的说明.................................................................................................5
3. 总述 ............................................................................................................................................... 6
3.1 CTorrent 的命令行参数的意义 ...........................................................................................6
3.2 CTorrent 的状态栏的意义 ...................................................................................................6
3.3 各个类实现的具体实例 .....................................................................................................7
3.4 BT 协议的特性和 CTorrent 的实现情况........................................................................... 8
4. 源代码分析................................................................................................................................. 10
4.1 ctorrent.cpp.........................................................................................................................10
4.2 downloader.cpp...................................................................................................................11
4.3 bencode.h............................................................................................................................13
4.4 bitfield.h..............................................................................................................................15
4.4.1 class BitField........................................................................................................... 15
4.5 btcontent.h.......................................................................................................................... 18
4.5.1 BTCACHE 结构体................................................................................................. 18
4.5.2 class btContent........................................................................................................ 18
4.6 btfiles.h............................................................................................................................... 30
4.6.1 Struct BTFILE .........................................................................................................30
4.6.2 Class btFiles.............................................................................................................31
4.7 btrequest.h...........................................................................................................................35
4.7.1 class RequestQueue.................................................................................................35
4.7.2 class PendingQueue.................................................................................................37
4.8 btstream.h........................................................................................................................... 38
4.8.1 class btStream..........................................................................................................38
4.9 bufio.h.................................................................................................................................40
4.9.1 class BufIo...............................................................................................................40
4.10 connect_nonb.h.................................................................................................................42
4.11 httpencode.h......................................................................................................................42
4.12 iplist.h............................................................................................................................... 44
4.12.1 struct _iplist........................................................................................................... 44
4.12.2 class IpList .............................................................................................................44
4.13 peer.h.................................................................................................................................45
4.13.1 宏 .......................................................................................................................... 45
4.13.2 struct _btstatus.......................................................................................................46
4.13.3 class btBasic .......................................................................................................... 46
4.13.4 class btPeer:public btBasic....................................................................................47
4.14 peerlist.h........................................................................................................................... 56
4.14.1 struct _peernode.................................................................................................... 56
4.14.2 class PeerList .........................................................................................................57
4.15 rate.h................................................................................................................................. 69
4.15.1 变量 ...................................................................................................................... 69
4.15.2 函数 ...................................................................................................................... 70
4.16 setnonblock.h....................................................................................................................70
4.17 sigint.h.............................................................................................................................. 71
4.18 tracker.h............................................................................................................................ 71
4.18.1 宏 .......................................................................................................................... 71
4.18.2 变量 ...................................................................................................................... 72
4.18.3 函数 ...................................................................................................................... 73
5. 后记 ............................................................................................................................................. 78
5.1 开源和 BitTorrent,不得不说的话 ................................................................................. 78
5.2 BT 的精神:共享,公平和宽容......................................................................................78
5.3 本篇文档的版权和莫做害群之马 ...................................................................................78
5.4 我的敬意 ........................................................................................................................... 79
5.5 结语 ................................................................................................................................... 79
图表目录
图表 1 main()函数流程图 ...................................................................................................... 10
图表 2 Downloader()函数流程图.......................................................................................... 12
图表 3 btFiles::_btf_recurses_directory()函数流程图...........................................................33
图表 4 btPeer::RequestPiece()函数流程图............................................................................ 52
图表 5 btPeer::Send_ShakeInfo()函数流程图....................................................................... 55
图表 6 PeerList::UnChokeCheck()函数流程图.....................................................................61
图表 7 算法 1 流程图............................................................................................................ 62
图表 8 算法 3 流程图............................................................................................................ 63
图表 9 PeerList::FillFDSET()函数流程图.............................................................................66
图表 10 PeerList::AnyPeerReady()函数流程图 .................................................................... 67
图表 11 btTracker::SendRequest()函数流程图 ......................................................................76
表格目录
表格 1 BitField::Except()函数逻辑表....................................................................................16
表格 2 m_shake_buffer[68]位填充情况................................................................................ 19
1. 前言
1.1 为什么要写这份文档
BitTorrent 点对点文件传输协议(以下简称 BT 协议)及其客户端应用大行其道的今天,各
种各样的客户端不胜枚举(可以参看 http://wiki.theory.org/BitTorrentApplications),而各种各
样的 BT 技术论坛讨论的却都是有关客户端软件如何使用的问题,有关底层协议细节和实现
方案的讨论少之又少。我碰巧有机会研究过一阵 BT 协议的原理,也看过一部分源代码
(CTorrent),虽然现在不再继续 BT 方面的研究了,但有感于当初看代码时遇到的资料的匮
乏的窘境,便决心把自己的理解和心得写出来,算是自己的一份总结(这也是我的本科毕业
论文),也希望帮助对 BT 协议实现有兴趣的人尽快上手,少走弯路。
有关 BT 协议的论述主要有三篇文章:
1,BT 官方网站上的协议解释:http://www.bittorrent.org/protocol.html。
2,Bittorrent Protocol Specification,http://wiki.theory.org/BitTorrentSpecification。
3,Incentives Build Robustness in BitTorrent,http://www.bittorrent.com/bittorrentecon.pdf。
这三篇文章从不同方面给出了 BT 协议从算法到实现的一个较为简略的描述。为了更深入地
理解 BT 协议,自己动手写一个 BT 客户端或阅读一个 BT 客户端的源代码的工作是必不可
少的。
1.2 客户端的选择
Bram Cohen 是 BT 协议的创建者。根据这份协议,他写了 BT 的第一个客户端,也就是
BitTorrent 公司的产品:BitTorrent。可以说,BitTorrent 的源码和 BT 协议是门当户对,要理
解协议,先从 BitTorrent 的源码开始是最好不过的了。
但 Bram Cohen 是用 Python 语言写的 BitTorrent,这给很多不懂 Python 的人(我也在内)带
来了很多麻烦:为了看懂一份源码而去新学一份计算机编程语言是不是有些不值得呢?
好在 BT 客户端是如此之多,我们有很大的选择空间。除了 Python,还有 Java(主要是 Azureus,
国外非常流行的多平台的客户端)和 C++(其它大部分客户端)写成的程序。
经过多方比较,我选择了 CTorrent 这个客户端。虽然 CTorrent 是用 C++写成的,但仅仅算
是一个轻量级(light-weighted)的 C++软件。它的库函数依赖型很小,只用到了 Open SSL
库用来计算哈希值,所以可以工作在 Linux, FreeBSD 和 MacOS 平台。CTorrent 没有图形界
面,工作在命令行模式。
另外,libtorrent(http://www.rasterbar.com/products/libtorrent.html)也是一个值得一看的客户
端。Libtorrent 用到了很多 C++的模板库(主要是 boost),客户端的性能非常好,而且还提
供库函数给其它程序调用。只是作者的 C++水平实在太低,对这种重量级的软件掌握不了。
1.3 CTorrent 简介
CTorrent 是由 YuHong 写的一个 BT 客户端。它的代码大部分都可以看作是 C 代码,只是用
到了 C++的类概念,还有一小部分构造函数,析构函数,函数和操作符重载的代码。不懂
C++ 的 人 只 需 有 一 些 C++ 的 基 本 知 识 就 完 全 能 看 懂 源 代 码 了 。 CTorrent 的 主 页 是
http://ctorrent.sourceforge.net,它遵循 GPL。
作 者 在 CTorrent 主 页 上 称 自 己 为 YuHong , 这 里 有 一 篇 他 写 完 CTorrent 后 发 的 帖 子 :
http://www.freebsdchina.org/forum/viewtopic.php?p=39082,想必是中国人吧。
用户在使用时发现 CTorrent 有一些 bug,一个比较明显的例子是 CTorrent 下载完成后不会立
即把缓存中的数据写入硬盘,这样如果按下 Ctrl-C 结束程序的话会造成数据的不完整。
CTorrent 的最新版本是 1.3.4(2004 年 9 月 7 日发布),作者后面就没有再发布新版本,软件
的一些问题也没有得到修正。
虽然有一些 bug,但得益于 CTorrent 是开源项目,很快就有人为 CTorrent 写了一些补丁
(http://sourceforge.net/tracker/?group_id=91688&atid=598034)。其中一个叫 Dennis Holmes
的人贡献颇多,他为 CTorrent 打了很多 patch,然后重新发布,取名为 Enhanced CTorrent。
Enhanced CTorrent 的 主 页 是 http://www.rahul.net/dholmes/ctorrent 。 目 前 已 经 更 新 到 了
ctorrent-dhn2 版本,这个版本配合 Dennis Holmes 用 Perl 写的一个 CTorrent Control Server,
可以实现对 Enhanced CTorrent 运行状况的监控。
这 篇 CTorrent 的 源 码 分 析 是 基 于 ctorrent-dhn1.2 版 本 的 , 原 因 是 由 于 我 查 看 Enhanced
CTorrent 较早,那时还没有 ctorrent-dhn2 版本,再加上自己偷懒,没有赶在 ctorrent-dhn2 发
布之前把文章写完……比较而言,dnh1.2 版本已经是一个相对稳定的版本了,dnh2 的改进
主要是在性能方面,而非 bug fix(容我再强词夺理一句,我简略看过 dnh2 版本的代码,在
dnh1.2 的基础上,看懂 dnh2 是没有问题的)。
另外,Dennis Holmes 虽然重新发布了 CTorrent,但他本人对原作者是极为尊敬的。在他的
dnh 版本中,原封不动地保留了原先代码的痕迹,自己的改动也加上了相应的注释。虽然
CTorrent 有一些 bug,但正如 Dennis Holmes 所言:谁又说其它客户端没有 bug 呢?我的这
篇源码分析也统一称 CTorrent 和 Enhanced CTorrent 为 CTorrent,只有在需要两个版本比较
时才区分开来。
2. 准备工作
2.1 知识储备
要看懂 CTorrent 源码和本篇源码分析,读者需要具备如下知识:
1,前面列举的 BT 协议的大致了解。
2,网络 socket 编程方面的基本知识,主要是 select()函数的使用。
3,至少会 C 语言,了解 C++的基本使用方法(主要是类,构造函数,析构函数和重载)。
2.2 我对本篇源码分析的说明
1, 源代码中如果出现一些乱码(特别是在终端中查看时),设置:
$export LANG=C
即可看到原作者写的中文注释。
2, 源码解说一般采取流程图的形式,有一些函数的具体功能不是很集中,画流程图也表示
不出前后联系来,就直接写了步骤分析。有些源码比较晦涩的,会直接分析源代码。
3, 源代码中的全部变量都有分析。大部分函数都有说明,少数特别简单的函数和见名知意
的函数没有说明。
4, 源代码中看似简单的表述实际蕴含着及其严格的操作要求(例如宏 P_HANDSHAKE 的
意思是可以进行握手通信了,而不是正在进行握手通信或者已经完成握手通信了)。所
以必须正确理解源代码各个宏,变量,函数的确切含义,才能真正理解程序的流程和作
用。
5, 分析源码的最终目的是彻底理解 BT 协议的实现结构,以及 BT 通信性能卓越的原因。
虽然程序中涉及 BT 协议算法的只有几个函数,但这几个函数是在其它大量代码的基础
上构建的。一些有关种子文件的制作和解析的代码虽然看似和 BT 通信关系不大,但若
前面的基础没有理解正确,会给后面的算法分析带来很大的麻烦。
6, 原作者的 C 语言技巧相当高,enjoy it!
7, 本文中“函数”指的是当前正在分析的函数,而“程序”指的是整个 CTorrent 程序。
8, 本文中“消息”指的是 peer 发来的固定格式的消息,例如 piece 消息,bitfield 消息等。
“数据”指的是客户端要下载的东西,例如一个游戏,一段视频等。
9, 英文中种子文件有很多说法,如.torrent file, metainfo file,本文中均用它们的中文名:种
子文件。
10,英文中关于 BT 协议的最小数据单元有很多说法,如 slice,block,subpiece,本文中使用
CTorrent 源代码中的说法:slice。
3. 总述
3.1 CTorrent 的命令行参数的意义
-h/-H:显示帮助命令
-x:只解码并显示种子文件信息,不下载。
-c:只检查已下载的数据,不下载。
-v:打开 debug 调试输出。
下载完毕后的做种时间(单位:小时),默认为 72 小时。
绑定端口,默认为 2706。
重命名下载的文件,若是下载的是多个文件,则 sava_as 是包含多文件的
下载选项:
-e int
-p port
-s save_as
目录。
-C cache_size
-f
-b bf_filename
-M max_peers
-m min_peers
-n file_number
-D rate
-U rate
-P peer_id
下载数据文件示例:
ctorrent -s new_filename -e 12 -C 32 -p 6881 eg.torrent
制作种子文件示例:
ctorrent -t file_to_make.avi -s a.torrent -u protocol://address/announce
缓存大小,默认为 16MB。
强制做种模式,不进行 SHA1 HASH 检查。
piece 位图文件名,详见 BitField::SetReferFile()。
客户端最多与多少个 peer 通信。
客户端至少与多少个 peer 通信。
多文件下,选择哪个文件去下载(例如第二个文件 file_number 就为 2)。
限制最大下载速率(单位:KB/s)。
限制最大上传速率(单位:KB/s)。
客户端通信的 ID,默认为-CD0102-。
3.2 CTorrent 的状态栏的意义
CTorrent 运行时输出格式如下:
$/ 1/10/40 [3/148/148] 2MB,1MB | 48,20K/s | 80,40K E:0,1
各项意义为:
/:表明客户端正在工作的符号,以”- \ | /”循环。
1:种子数目。
10:客户端正在通信的非种子的 peer 数目。
40:tracker 服务器知道的 peer 数,也是整个 bt 通信群的 peer 数。
3:客户端已经下载的 piece 数目。
148:数据文件全部的 piece 数目。
148:客户端可以得到的 piece 数目,若此数小于全部 piece 数目则不会下载到完整的数据。
2MB:客户端已经下载的数据量。
1MB:客户端正在上传的数据量。
48:客户端的平均下载速率(KB/s)。
20:客户端的平均上传速率(KB/s)。
80:客户端的即时下载速率(KB/s)。
40:客户端的即时上传速率(KB/s)。
0:客户端与 tracker 服务器通信失败的次数。
1:客户端与 tracker 服务器通信成功的次数。
3.3 各个类实现的具体实例
CTorrent 程序使用了 C++面向对象的特性。在程序中有一些类的实例(instance),分别代表
了一个 BT 通信群中的各个对象。
3.3.1 BTCONTENT
BTCONTENT 是 btContent 类实现的实例。它在程序中代表种子文件和本地数据文件。
3.3.2 PENDINGQUEUE
PENDINGQUEUE 是 PendingQueue 类实现的实例。它在程序中代表由于与 peer 的暂时通信
中断而搁置等待的 slice 链表的队列。
3.3.3 IPQUEUE
IPQUEUE 是 IpList 类实现的实例。它在程序中代表从 tracker 服务器传来的 peer 列表的链表。
3.3.4 Self
Self 是 btBasic 类实现的实例。它在程序中代表客户端自己。
3.3.5 WORLD
WORLD 是 PeerList 类实现的实例。它在程序中代表所有正在与客户端通信的 peer 的链表
3.3.6 Tracker
Tracker 是 btTracker 类实现的实例。它在程序中代表 tracker 服务器。
3.4 BT 协议的特性和 CTorrent 的实现情况
BT 下载之所以性能出众是由 BT 协议所规定的一系列机制所保证的。判断一个 BT 下载软
件性能优秀与否则是看这个软件对 BT 协议中下载机制的执行情况。BT 协议主要规定了两
大类机制保证其性能(详细信息请参照” Incentives Build Robustness in BitTorrent”):
3.4.1 Piece 选择机制
3.4.1.1 初始模式(Initial Mode):Random First Piece。
当客户端刚开始运行时,它一个完整的 piece 也没有,这时需要尽快下载到一个 piece 以便
可以提供上传服务。此时的算法为:第一个随机 piece。客户端会随机找到一个 piece,然后
下载。
CTorrent 随机选择 piece,而且更进一步采取了一种加速下载的办法:虽然此时客户端没有
piece,但应该有向其它 peer 的申请 slice 的队列了。客户端只要比较这些队列哪个最短,优
先下载最短的队列即可最快获得第一个 piece。
函数 PeerList::Who_Can_Duplicate()实现了此算法的代码。
3.4.1.2 一般模式(Normal Mode):Strict Priority 和 Rarest First。
1,严格优先(Strict Priority)
一旦某个 slice 被申请,则这个 slice 所在的 piece 中的其它 slice 会先于其它 piece 的 slice 被
申请。这样做可以尽量使最先申请的 piece 最先被下载完毕。
这条规则看似简单而且公平,但实现起来非常困难:BT 协议规定一个 piece 中的多个 slice
可以向多个 peer 申请,而客户端又同时从多个 peer 处申请了多个 piece 中的 slice,这么多
数据传输队列同时进行,要保证严格优先是非常困难的。
CTorrent 在设计时采取了一种比较简单的方法:一旦向某个 peer 申请了某个 slice,则这个
piece 中的所有 slice 均向这个 peer 申请。为了保证尽快将一个 piece 下载完成,CTorrent 会
找出当前正在与之通信的那个 peer(正在通信的 peer 通常比较活跃),然后把所有 slice 请求
队列中最慢的那个队列找出来,交给这个 peer 去下载。
函数 PeerList::Who_Can_Abandon()实现了此算法的代码。
2,最少优先(Rarest First)