分类: Java
2007-12-05 16:12:14
-------- 熊红阳
Snark是国外一个开源Java的项目,实现了BitTorrent协议,通过分析此项目的源程序,可以更利于我们更加深入的了解当前流行的BT软件的原理,进而可以指导我们的p2p软件方面的开发。
一、在分析Snark之前,我们先简单了解BT协议, 如果想了解更多请到 。
这个协议本身可以分为以下几个部分:
1.数据编码,BT采用bencoded编码,我们可以不用细究这种编码的具体细节,我想编码目的无非是方便数据传输及解析,感觉有点类似于Java中序列化,就是一个打包和解包的过程。
2.元文件,就是我们通过所说的种子文件,通常以.torrent后缀结尾,这个文件就是描述Tracker的url,资源文件的长度、sha摘要值、文件长度、分块大小、块数等。
3.Tracker,tracker通俗的讲起一个资源定位作用,就是让客户端彼此能够发现,Tracker本身其实就是简单的HTTP服务器,它和客户端交互也是以这种协议。
4. 客户端(以下我们简称Peer)之间的互连,包括Peer之间的握手、阻塞,产生兴趣,请求及传送文件块。
5. Peer之间的选择算法,包括下载策略、结束策略、阻塞优化、反冷落策略等。
分析前先让我们简单的了解一下BT原理:
<图 一>
说明:上图描述了一个简单的文件分发过程,首先Seeder具有全部文件块1,2,3,4,5,Seeder将1,2发给A,将3,4,5发给B,然后A,B交换已方没有的块,最后A,B都有了全部的文件块,其实BT的基本思想就是这样的。
二、Snark分析
<图二>
Snark整体架构还是蛮清晰的,主要的类及相互关系如上图Peer代表与自身连接的所有其它对等协助者,Peer是通过TrackerClient类从Tracker上请求过来的。
与Peer相关的类和对象有PeerConnectionIn in,PeerConnectionOut out,PeerState,PeerState包含PeerConnectionIn和PeerConnectionOut,Peer包含PeerState,PeerConnectionIn的run当中封装了网络协议包的读入和解析,使用线程不断读取对方的网络输入,PeerConnectionOut的当中封装了一个队列sendQueue,以及网络协议的写出方法sendXXX,使用线程run方法不断向外写协议包.
Peer的管理是通过两个类来完成的,一个类是PeerCheckerTask定时完成对Peer的检查,更新Peer自身的信息,基主要功能是通过各种条件判断来改变对连接的Peer的状态(阻塞还是非阻塞),我想阻塞、非阻塞的意义在于控制带宽及优化Peer间的数据传输,Snark这块的实现不难明白,主要看上传数是否超过最大限制,如果超过需要一个个检查Peer信息,如果对方无意从本方获取数据加以阻塞,如果对方阻塞自己也加以阻塞,如果从对方下载不到数据加以阻塞,如果双方都没有阻塞找一个下载量最小的加以阻塞,另一个方面就是当上传数小于最大限制数时需要解除对一部分Peer的阻塞,如果对方有需要从本方获取数据块就解除阻塞;另外一个类是PeerCoordinator,配合PeerMonitorTask定时对所有的Peer进行检查,完成网络的自组织和传输的优化,其中PeerCoordinator中unchokePeer方法是关键方法。
在块的选择请求方面,Snark并没有按照最少优先原则选择下载块,按顺序找一个想要的块,开一个块大小(一般256K,最后一块可能略小)的缓冲,每次请求16k,当这个缓冲填满时则写到硬盘上。
数据的存储是通过Storage类完成的,存储这块并不复杂,需要通过sha摘要值判断下载的数据块是否正确,Storage创建时如果是第一次下载就会为每个文件分配一个同样大小空的硬盘空间,方便后面读写,如果断点续传,程序会计算每块的sha值和正确的相比较,如果相等就会设置该块已下载的标志,不用再请求别的Peer。
整个程序的起始于Snark类的main方法, 它通过相应种子文件的Http url发起请求,获取数据解析成MetaInfo对象方便使用,通过MetaInfo对象创建Storage对象,用于本地文件读写,创建PeerCoordinator对象,为今后所有Peer的管理类,最后创建TrackerClient(实际为一线程) 并启动,TrackerClient的作用很简单,隔一段时间(这里是一分钟)向Tracker发一次请求,获取在线的其它Peer的信息,通过PeerCoordinator的addPeer方法连接上其它的Peer,Snark选择Peer时做得很简单,没有进行各方面的参数匹配,只是机械的连接而已。
一个客户端(也是一个Peer)连接另一个Peer时,首先互发握手信息,这里Peer类中有一个handshake方法起此作用,只要协议串及Peer ID匹配,这两个Peer就正式建立连接了,建立连接后Snark会为一个Peer分配两个线程, PeerConnectionIn负责读数据解析协议,PeerConnectionOut负责写数据,然后Peer之间马上互发一个位域(一组标志本方所拥有文件的二进制数组),Peer收到对方位域后就确定对对方的态度,BT称为interested or not interested,有兴趣还是没兴趣,这一点Snark中算法比较简单,只是判断本方没有的块中只要对方有一块,就会对对方感兴趣,然后本方就会向对方发送块piece请求,Peer收到块请求后,如果处于choking(阻塞)对方状态,就不予处理,如果没有就发送请块给对方,对方收到块后,只需要交给Storage写到硬盘。
三、Snark存在的不足
1.开启线程过多,当连接数增大,会占用大量系统资源造成性能下降。
2.空连接较多,相当长时间并无数据传输占用系统资源,本人觉得这也许是所有BT软件的一个通病。
3. 请求和选择算法的设计上并不是很智能,难于产生所谓的Pareto Efficiency(帕累托效率)。
四、总结
一句话,Snark麻雀虽小五脏俱全,虽然诸多地方并不是很完美,但它提供了一个很好程序模型,便于我们更深入的了解p2p。