本文由春华秋实编辑,地址http://hi.baidu.com/3600/blog/item/ab02a50f5ccf08eaab64578d.html
*****************************************************************************
转自:jijian91和小z 的关于bnbt的源码分析,由于原文连载较多,就直接转了过来
原文地址
http://jijian91.com/blog/internet/software/bittorrent‘
因为要对一个以BT(bnbt)为核心的系统进行优化改进,上网搜了一下BT服务器和客户端的源码分析资料,所获很少(有价值只有信仰和小马哥)。只好自己总结,希望能留下点有用的。
要分析和改进BT,先要了解BT的架构和先说说BT的架构。
按照时间顺序,依次发生的是
在bt体系运转过程中,tracker处于中心,是”全局单点”,所有的peer都要在tracker上注册、得到其他peer,并周期性地访问tracker更新信息。所以,如果tracker出现故障,peer间无法沟通,bt崩溃。
为了解决这个隐患,也为了加速peer间相互发现,搞了个DHT网络,bitcomet和BitTorrent Beta 4.1.1都支持。也就是允许peer互相交换peer列表,这样,即使tracker垮了,bt客户端也能通过已有的peer连接不断得到其他peer。但目前,DHT只是个补充。
制作torrent文件的客户端是第1个seed(原始种子)。其他peer开始都是lecher,下载完毕变成seed。全部peer中,seed的比例大则下载总速度快。
如果原始种子离开过早或其他peer在成为seed以后立刻离开,可能造成全部peer的数据都构不成完整文件,这个bt文件就是无法下载的废文件。所以,seed的培养是bt中的重要问题。
为解决种子问题,BTPLUS S-5.5搞了个超级种子(super seed)概念。即:
当一个客户端启用超级种子模式时,他将伪装成一个没有数据的普通客户端。当其他客户端连接时,超级种子模式将传输给他一个 从没被 传输出去的块,当所有文件块都被发出时,将意味着不需要你这个种子其他下载者就可以完成下载进程.这样将大大降低一个文件块被下载多次的几率并提高做种效 率。这个方法将提高做种效率通过两者间的高效率数据传输,从而降低多余的数据传送。并限制对贡献少的下载者 的数据传送。使用这个功能后只要上传105%的总大小就可以培养 出新种子.这比普通种子的效率高1.5 到 2倍。
但超级种子模式会极大地降低对外传输数据流量,一般只建议Torrent文件发布者(原始种子)使用。
解决peer成为seed以后立刻离开的问题,才能提高bt体系整体效果,但目前尚无好的办法。
bt服务器主要就是tracker。我们用的是开源的bnbt。这是后文分析和改进的重点。
bt客户端就复杂了。
一是实现很多,只要不违反bt协议,实现原理上可以完全不同。但实际上,大多数bt客户端都是在bittorrent的基础上改进的。所以后文主要分析bittoreent客户端源码。
二是bt客户端的代码比服务器复杂得多,这与一般的情况刚好相反。
三是用户可以使用任何客户端,不可能要求用户改用我们改进的客户端。所以优化改进只能以bt服务器为主。
bnbt是应用较广的开源BT服务器(BT tracker)。下面分析一下bnbt tracker的主要流程(注意:没有使用数据库):
bnbt源码全部是C++,程序起点是bnbt.cpp中的main函数。其中首先解析命令行参数,然后是与Windows服务相关的处理,最后调用bnbtmain函数。
bnbtmain函数也在bnbt.cpp中。主要解析配置文件、设置大量运营参数,然后创建CServer的实例gpServer,最后是在死循环中不断调用gpServer.Update().
CServer类是在server.cpp中定义的。
CServer构造函数的主要工作是绑定监听端口,实际是建立了一个server,最后创建了CTracker的实例m_pTracker.
CServer类的Update()函数主要功能是处理连接上来的BT客户端。如果客户端队列m_vecClients未满(缺省最大64),则为每个连接创建1个CClient实例,并将其压入m_vecClients尾部。然后,依次执行m_vecClients中各CClient对象的Update函数,并将返回值为true的对象释放掉。
CClient类是在client.cpp中定义的。其构造函数很简单,重点是Update()函数。Update()函数的功能是处理BT客户端的请求。具体说,从socket连接读取信息,按照HTTP协议进行了分析,转化成GET和POST请求调用CServer中CTracker的serverResponseGET()和serverResponsePOST()函数处理,将处理返回的结果再返给BT客户端。
CTracker类是在tracker.cpp中定义的。
serverResponsePOST()中只处理POST方式的”/upload.html”的请求,即上载页面。
serverResponseGET()是重点,处理以下请求:
通过上文的分析可以看出,bnbt(也就是tracker)实际是一个socket服务器,但模拟成web server。很有意思:)bnbt对外是100%的web server。所以,用java或php重新写一套bt服务器,运行在web server上是完全没有问题的。
/announce是bnbt实现的各http页面中最核心的,也是bt客户端唯一访问的页面。BT服务器与客户端之间是通过基于HTTP的bt协议进行通讯,在bnbt源码中由CTracker.serverResponseAnnounce()函数实现。下篇文章我们将专门谈论这个函数。
除了/announce以外,其他页面都是可选的,所以很多bnbt服务器只提供部分。有兴趣的话,可以参看这个德国服务器,功能提供得比较全。
由上篇bnbt tracker执行流程,我们知道bt tracker的核心是/announce。BT客户端与tracker之间的通讯以及tracker内部是如何处理的呢?
我们从BT客户端发起请求开始。BT协议的具体内容不多说,参见这里。
BT客户端首先读torrent文件,例如:
d8:announce35:http://192.168.0.1:2222/announce10:created by13:BitComet/0.9213:creation datei1192120049e8:encoding3:GBK4:infod4:ed2k16:魖趸?[T,胕@驋8:filehash20: 廹 C<`鮾}瀽穤茕蛯]6:lengthi43e4:name11:AUTORUN.INF10:name.utf- 811:AUTORUN.INF12:piece lengthi32768e6:pieces20: 廹 C<`鮾}瀽穤茕蛯]ee
其中,”http://192.168.0.1:2222/announce”是tracker URL,”AUTORUN.INF”是文件名,中间的乱码是文件的hash值,其他信息还有片段(piece)的长度(32K)、片段数、创建的软件、时间等。
BT客户端据此向tracker发送GET请求,例如:
GET http://192.168.0.1:2222/announce?info_hash=w%3D%1E%FB%A2%09%8C%E5k%2A%9F%A03PQ%3E%12%2CMq&peer_id=-BC0092-%89v%5E%0A%8Cf%EB%90%B1%0D%EE%DE&port=14479&natmapped=1&localip=192.168.0.65&port_type=lan&uploaded=0&downloaded=0&left=0&numwant=200&compact=1&no_peer_id=1&key=31526&event=started HTTP/1.1
其中,主要是文件的hash值(info_hash)、希望返回的bt客户端数量(numwant),以及本客户端的信息(peer id、port、已上传下载字节数等)。
bnbt在tracker_announce.cpp的CTracker.serverResponseAnnounce()函数中处理这个请求,具体流程是:
serverResponseAnnounce的处理过程比我想象的简单得多,它只是尽可能保证返回peer数量,并未对peer进行任何选择。换言之,tracker并不考虑返回的是seed,还是lecher。实际上,tracker根本就没有记录哪个peer是seed。因为BT客户端之间是建立双向传输连接互相传输数据的,所以BT的作者显然是想通过无为而治来达到自然的上下载平衡。
但在某些特殊的场景中,达不到这种理想平衡。比如,大量peer集中下载1个新文件,seed很少,绝大多数是完全没有数据的空lecher。 seed就会被淹没在空lecher的海洋中。每个peer得到的都是一堆空lecher,都不能下载到任何数据。而seed反而因为只有很少的peer 连接过来而空闲,从而使整个BT下载体系陷入停顿的状态,如同一个果料果冻,少数果料被包裹分隔,发挥不出作用。所以,我叫它“果冻效应”。后面我们将重点讨论果冻效应及其解决。
要优化BT服务器,除了分析BT体系结构和分析BT服务器源码,还需要简单了解一下BT客户端的动作。
BT架构很有意思,客户端比服务器复杂得多。现在有很多BT客户端,大多是在BitTorrent的基础上改造的,加上自己的优化,而且不开放源码。例如,国内最常见的BitComet就是如此。BitTorrent不甘于为别人做嫁衣,最近宣布停止开放源码。BitTorrent公开源码的最后一个版本是5.0.9,可以在这里下载。
BitTorrent客户端是用python写的!对不熟悉python语言的人来说(我就是),看得晕头晕脑。最后,我参考了小马哥的分析,做了个简单归纳。好在目标只是大致了解,不用仔细研究。
BitTorrent客户端主要完成以下工作:
其中,1在前面已经分析过了。4不是我们关心的重点。
BT客户端从tracker得到peer列表后,就要主动与这些peer建立连接,同时也接受其他peer的连接请求。这就是2的具体内容。BT客户端间建立的是对等连接,由哪一方主动发起并不重要。一旦建立,就可以双向传输文件。支持DHT网络的,还可以传输peer列表。
建立连接的2个BT客户端中,至少有一方是公网IP或两方在同一个局域网内。如果一方是公网IP,另一方在局域网内,则只能是局域网内的向公网的发 出连接请求。一个BT体系下,必须存在一定量的公网用户,否则就是产生相互无法连接的问题。国内的现实情况是,多数公司、网吧和教育网都是局域网用户,所 以局域网用户比例偏高。拨号上网、ISDN、ADSL用户使用的是动态公网IP,所以近年来的ADSL普及对此有缓解作用。要注意的是,公网和局域网用户 只是在建立连接的时候作用不同,在连接建立后传送文件时是没有区别的。
片断选择算法相当复杂,目的是挑选“最全局价值最大”的片段进行下来。阻塞算法也是很复杂的,用于调整上下载比例,鼓励用户多提供上载作贡献,惩罚投机者。但对于BT体系整体优化作用不大,可以暂时忽略。