BitTorrent(简称BT)是一个文件分发协议,即每个下载者在下载的同时会向其他下载者提供已下载的数据。在传统的FTP、HTTP协议中,所有的下载者都向中心服务器请求数据,各个下载者之间没有任何交互。这会出现一个问题,当非常多的下载者同时下载同一个数据时,由于中心服务器的处理能力和带宽限制,导致下载数据非常之慢,甚至有些用户都连不上服务器。BT下载则完全不同,由于其下载文件共享下载的特性,使得下载者越多下载速度越快。
基于BitTorrent协议的文件分发系统由以下部分组成: (1)Web服务器。 (2)种子文件。 (3)Tracker服务器。 (4)原始文件提供者。 (5)网络浏览器。 (6)下载者。
Web服务器上保存着种子文件,下载者需要首先从web服务器上下载种子文件。种子文件又叫元文件(metafile),它保存了共享文件的信息,如文件名、文件大小、Tracker服务器的地址等。种子文件非常小,一般大小为1GB的共享文件,其种子文件不足100KB,种子文件以.torrent为后缀。种子文件中的Tracker服务器地址是记录下载该文件的所有节点的IP和端口。种子文件是由原始文件提供者制作的。制作过程:首先下载BT客户端,然后新建一个BT种子任务,选中要共享的文件,再点击制作即可完成种子文件的制作。用户将种子文件上传到网络中,其他用户就可以通过该种子文件获取到对应的文件内容。
用户使用种子文件下载的过程:用户首先下载BT种子文件,然后在迅雷(或其他下载客户端)客户端中新建一个BT任务,选中种子文件,点击开始下载即可。原理是这样的,客户端首先解析种子文件,获取待下载的共享文件的一些信息,其中包括Tracker服务器的地址。然后客户端连接Tracker获取当前下载该文件的所有下载者的IP和端口。之后客户端根据IP和端口连接其他下载者,从它们那里下载文件,同时把自己已下载的部分提供给其他下载者下载。
共享文件在逻辑上被划分为大小相同的块,称为piece,每个piece的大小通常为256KB。对于共享文件,文件的第1字节到第256K(即262144)字节为第一个piece,第256K+1字节到第512K字节为第二个piece,依此类推。种子文件中包含有每个piece的hash值。BT协议规定使用Sha1算法对每个piece生成20字节的hash值,作为每个piece的指纹。每当客户端下载完一个piece时,即对该peice使用Sha1算法计算其hash值,并与种子文件中保存的该peice的hash值进行比较,如果一致即表明下载了一个完整而正确的piece。一旦某个piece被下载,该piece即提供给其他peer下载。在实际上传和下载中,每个piece又被划分为大小相同的slice,每个slice的大小固定为16KB(16384字节)。peer之间每次传输以slice为单位。
从以上描述可以得知,待开发的BT软件(即BT客户端)主要包含以下几个功能:解析种子文件获取待下载的文件的一些信息,连接Tracker获取peer的IP和端口,连接peer进行数据上传和下载、对要发布的提供共享文件制作和生成种子文件。种子文件和Tracker的返回信息都以一种简单而高效的编码方式进行编码,称为B编码。客户端与Tracker交换信息基于HTTP协议,Tracker本身作为一个Web服务器存在。客户端与其他peer采用面向连接的可靠传输协议TCP进行通信
种子文件和Tracker返回的信息都是经过B编码的。要解析和处理种子文件以及Tracker的返回信息,首先要熟悉B编码的规则。B编码共有4种类型:
种子文件的关键字如下表:
名----------字 | 类--------------型 | 意---义 |
---|---|---|
announce | string | TrackerServer的地址 |
announce-list | List of list | TrackerServer分组列表 |
info | Dict(k/v集合) | 该关键字对应的值是一个字典,该关键字对应的值是一个字典,它有两种模式,“singel file”和“multiple file”,文件模式和多文件模式。单文件模式是指待共享的文件只有一个,多文件模式是指提供共享的不止一个文件,而是两个或两个以上。如使用BT软件下载一部影片时,影片的上下部可能分别放在不同的文件里 |
publisher | string | 种子发布者名称 |
publisher-url | string | 种子发布者URL |
nodes | list | DHT(分布式哈希表)的节点列表 |
encoding | string | 种子文件的字符编码,如UTF-8 |
其中种子文件中最重要的关键字是info,无论是单文件模式还是多文件模式,该字典都包含关键字如下表所示。
---关键字---- | 含 义 |
---|---|
piece length | 每个piece的长度,它的值是一个B编码的整型,该值通常为i262144e,即256K,也有可能为512K或128K |
pieces | 对应的值为一个字符串,它存放的是各个piece的hash值,这个字符串的长度一定是20的倍数,因为每个piece的hash值的长度为20字节 |
private | 该值如果为1,则表明客户端必须通过连接Tracker来获取其他下载者,即peer的IP地址和端口号;如果为0,则表明客户端还可以通过其他方式来获取peer的IP地址和端口号,如DHT方式。DHT即分布式哈希表(Distribute Hash Tabel),它是一种以分布式的方式来获取peer的方法,现在许多BT客户端既支持通过连接Tracker来获取peer,也支持通过DHT来获取peer。如果种子文件中没有private这个关键字,则表明不限制一定要通过连接Tracker来获取peer |
对于单文件模式的种子文件,info的值还含有的关键字如下表所示。
--关键字-- | 含 义 |
---|---|
name | 共享文件的文件名,也就是要下载的文件的文件名 |
length | 共享文件的长度,以字节为单位 |
md5sum | 可选,它是共享文件的md5值,这个值在BT协议中根本没有使用,所以不必理会 |
对于多文件模式的种子文件,info的值还含有的关键字如下表所示
关键字 | 含义 |
---|---|
name | 存放所有共享文件的文件夹名 |
files | 它的值是一个列表,列表中含有多个字典,每个共享文件为一个字典。该字典中含有三个关键词 |
Files的每个共享文件为一个字典字典的关键词如下表所示
关 键 词 | 含 义 |
---|---|
length | 共享文件的长度,以字节为单位 |
md5sum | 可选,同上 |
path | 存放的是共享文件的路径和文件名 |
完成解析种子文件并从中获取Tracker服务器的URL后,即可开始与Tracker进行交互。与Tracker进行交互主要有两个目的:一是将自己的下载进度告知给Tracker以便Tracker进行一些相关的统计;二是获取当前下载同一个共享文件的peer的IP地址和端口号。 客户端使用HTTP协议与Tracker进行通信。Tracker通过HTTP GET方法获取请求,请求的构成为Tracker的URL后面跟一个?以及参数和值对,如http://tk.greedland.net/ announce? param1= value1¶m2= value2。 在客户端发往Tracker的GET请求中,通常包含参数下表所示。
---参数--- | 含 义 |
---|---|
info_hash | 与种子文件中info关键字对应的值,通过Sha1算法计算其hash值,该hash值就是info_hash参数对应的值,该hash值的长度固定为20字节 |
peer_id | 每个客户端在下载文件前以随机的方式生成的20字节的标识符,用于标识自己,它的长度也是固定不变的 |
port | 监听端口号,用于接收其他peer的连接请求 |
uploaded | 当前总的上传量,以字节为单位 |
downloaded | 当前总的下载量,以字节为单位 |
left | 还剩余多少字节需要下载,以字节为单位 |
compact | 该参数的值一般为1 |
event | 它的值为started、completed、stopped其中之一。客户端第一次与Tracker进行通信时,该值为started;下载完成时,该值为completed;客户端即将关闭时,该值为stopped |
ip | 可选,将客户端的IP地址告知给Tracker,Tracker可以通过分析客户端发给Tracker的IP数据包来获取客户端的IP地址,因此该参数是可选的,一般不用指明客户端的IP |
numwant | 可选,希望Tracker返回多少个peer的IP地址和端口号。如果该参数缺省,则默认返回50个peer的IP地址和端口号 |
key | 可选,它的值为一个随机数,用于进一步标识客户端。因为已经由peer_id来标识客户端,因此该参数一般不使用 |
trackerid | 可选,一般不使用 |
Tracker服务器的返回信息是一个经过B编码的字典。它含有关键字如下表所示。
----关键字---- | 含 义 |
---|---|
failure reason | 该关键字对应的值是一个可以读懂的字符串,指明GET请求失败的原因,如果返回信息中含有这个关键字,就不会再包含其他任何关键字 |
warnging message | 该关键字对应的值是一个可以读懂的警告字符串 |
interval | 指明客户端在下一次连接Tracker前所需等待的时间,以秒为单位 |
min interval | 指明客户端在下一次连接Tracker前所需等待的最少时间,以秒为单位 |
tracker id | 指明Tracker的ID |
complete | 一个整数,指明当前有多少个peer已经完成了整个共享文件的下载 |
incomplete | 一个整数,指明当前有多少个peer还没有完成共享文件的下载 |
peers | 返回各个peer的IP和端口号,它的值是一个字符串。首先是第一个peer的IP地址,然后是其端口号;接着是第二个peer的IP地址,然后是其端口号;依此类推 |
以下是一个发往Tracker服务器的HTTP GET请求的示例: http://tk.greedland.net/announce?info_hash=01234567890123456789& peer_id=01234567890123456789&port=3210&compact=1&uploaded=0&downloaded=0&left=8000000&event=started 以下是一个Tracker服务器回应的示例: d8:completei100e10:incompletei200e8:intervali1800e5:peers300:......e 其中,“......”是一个长度为300的字符串,含有50个peer的IP地址和端口号。IP地址占4字节,端口号占2字节,即一个peer占6字节。
peer之间的通信协议又称为peer wire protocal,即peer连线协议,它是一个基于TCP协议的应用层协议。 为了防止有的peer只下载不上传,BitTorrent协议建议,客户端只给那些向它提供最快下载速度的4个peer上传数据。简单地说就是谁向我提供下载,我也提供数据供它下载;谁不提供数据给我下载,我的数据也不会上传给它。客户端每隔一定时间,比如10秒,重新计算从各个peer处下载数据的速度,将下载速度最快的4个peer解除阻塞,允许这4个peer从客户端下载数据,同时将其他peer阻塞。 一个例外情况是,为了发现下载速度更快的peer,协议还建议,在任一时刻,客户端保持一个优化非阻塞peer,即无论该peer是否提供数据给客户端下载,客户端都允许该peer从客户端这里下载数据。由于客户端向peer上传数据,peer接着也允许客户端从peer处下载数据,并且下载速度超过4个非阻塞peer中的一个。客户端每隔一定的时间,如30秒,重新选择优化非阻塞peer。 当客户端与peer建立TCP连接后,客户端必须维持的几个状态变量如下表所示。
----状态变量---- | 含 义 |
---|---|
am_chocking | 该值若为1,表明客户端将远程peer阻塞。此时如果peer发送数据请求给客户端,客户端将不会理会。也就是说,一旦将peer阻塞,peer就无法从客户端下载到数据;该值若为0,则刚好相反,即表明peer未被阻塞,允许peer从客户端下载数据 |
am_interested | 该值若为1,表明客户端对远程的peer感兴趣。当peer拥有某个piece,而客户端没有,则客户端对peer感兴趣。该值若为0,则刚好相反,即表明客户端对peer不感兴趣,peer拥有的所有piece,客户端都拥有 |
peer_chocking | 该值若为1,表明peer将客户端阻塞。此时,客户端无法从peer处下载到数据。该值若为0,表明客户端可以向peer发送数据请求,客户端将进行响应 |
peer_interested | 该值若为1,表明peer对客户端感兴趣。也即客户端拥有某个piece,而peer没有。该值若为0,表明peer对客户端不感兴趣 |
当客户端与peer建立TCP连接后,客户端将这几个变量的值设置为。 am_chocking = 1。 am_interested = 0。 peer_chocking = 1。 peer_interested = 0。 当客户端对peer感兴趣且peer未将客户端阻塞时,客户端可以从peer处下载数据。当peer对客户端感兴趣,且客户端未将peer阻塞时,客户端向peer上传数据。 除非另有说明,所有的整数型在本协议中被编码为4字节值(高位在前低位在后),包括在握手之后所有信息的长度前缀。
网络协议一般都要求具有较高的传输效率,BT协议就是采用了流水线作业来提供传输效率的。 当客户端向peer发送数据请求时(即发送request消息),一次请求多个slice(即一个数据包发送多个request消息请 求多个slice)。假如客户端一次只发送一个slice请求,则peer给客户端发送完一个slice的数据后进入等待,等待客户 端发送新的数据请求。如果一次发送多个slice请求,则peer发送完一个slice后接着发送下一个slice,从而避免了等待 提高数据传输的效率。 HTTP协议的1.1版本就广泛地采用流水线作业的思想,大大提高了浏览器和Web服务器之间的传输效率。
片段选择有四大策略,良好的策略对于提高下载速度至关重要。
一、一旦向某个peer发送对某个piece中的slice的请求后,则该piece中的其他slice也从该peer处下载,这样可以 尽快地下载到一个完整的piece。因为某个peer拥有某个piece中的一个slice,则它必定拥有该piece的其他slice,并且 如果peer愿意发送一个slice给客户端,它也应该愿意发送piece中的其他slice给客户端。
二、最少优先。即某个piece在所有peer中拥有率最低,则优先下载该piece。此做法可以防止拥有此piece的 peer突然离开,导致某个piece的缺失,从而当前如何一个参与下载的peer都不能下载到一个完整的文件;如果下载了 某些拥有率较低的piece,则其他很多peer会向客户端请求数据,而要想从客户端下载到数据,那些peer就要提供数据 给客户端下载,这样对于提高客户端的下载速度也是有帮助的。对于这个共享系统而言,优先下载拥有率低的piece 可以使得整个系统提高每个piece的拥有读,整个系统会趋向与最优。如果所有peer优先下载拥有率较高的piece,则 piece拥有率更低,可能会导致很多peer得不到完整的文件。
三、随机选择第一个要下载的piece。开始下载时,不能采用最少优先策略。因为如果piece的拥有率很低,则 下载到这个piece就相对较难。如果随即选择一个piece,那么更容易下载到该piece,一旦客户端下载到一个完整 piece,就可以提供给其他peer下载,而由于客户端向其他peer上传数据,会倒在其他peer对客户端解除阻塞,从而 有利于在起始阶段获得较高的下载速度。当在下载到一些piece后,客户端 应采用最少优先策略来下载数据,这虽然 会导致客户端的下载速度在短期内有所下降,但随后下载速度会有较大提高。
四、最后阶段模式。有时,从一个传输速度很慢的peer处下载一个piece会花费很长时间,在下载的过程中不是 什么大问题,但在下载接近完成时,如果发生这种情况,会导致客户端迟迟不能下载完成。为了解决这个问题,在 最后阶段,客户端向所有peer发送对这个piece的某些slice请求,一旦收到某个peer发来的slice,则向其他peer发 送cancel消息,只从当前这个peer处下载。
BT并不集中分配资源,每个peer有责任尽可能地提高自己的下载速度。peer从它可以连接的peer下载文件,并根据对方提供的下载速率给予同等的上传回报,对于合作者,提供上传服务,对于不合作的,就阻塞对方。阻塞是一种临时拒绝上传的策略,虽然上传停止了,但是下载仍然继续。在解除阻塞时,连接并不需要重新建立。因为阻塞过程中只是拒绝传输piece消息,其他消息,如have消息,interested消息仍可以传输。阻塞算法虽然不是BT协议一部分,但是它对提高性能是必要的。
每个客户端一直与固定数量的peer保持疏通(通常是4个),那么以什么方式来决定是否保持与某个peer疏通呢?通常的做法是,严格地根据当前的下载速度来决定哪些peer应该保持疏通。但计算当前下载速度是个大难题。当前的实现通常是计算最近10秒从每个peer处下载数据的速度。以10秒为间隔重新选择保持疏通(即解除阻塞)的peer,是为了避免频繁地阻塞和解阻塞,造成资源的浪费。实践表明,10秒足以使下载速度达到最大。
如果只是简单地为提供最高下载速率的4个peer提供上载服务,那么就没有办法发现那些空闲的连接是否有更好的下载速度。为了解决这个问题,在任何时候,每个peer都拥有一个称为“optimistic unchoking(优化非阻塞)”peer,这个连接总是保持疏通状态,而不管它的下载速率是多少。每隔30秒,重新选择一个peer作为优化非阻塞peer。30秒足以让该peer的上载能力达到最大。
一旦某个peer完成了下载,它不能再通过下载速率(因为下载速率已经为0了)来决定为哪些peer提供上载了。目前采用的解决办法是,优先选择那些从它这里得到更好下载速率的peer,保持与它们疏通。这样做的理由是尽可能的利用上载带宽。一旦某个peer完成了下载,那么它也就成为了种子。种子拥有一份完整的文件拷贝,并提供给其他peer下载。为了整个系统的性能,每个peer在完成下载后应该作为种子存在一段时间,作为对整个系统的回报。原始种子,即最初提供文件进行共享、并制作生成了种子文件,并把种子文件发布到Web服务器的种子,它至少应该存在到系统中生成另外一个种子时才能离开,否则当前参与下载的所有peer都不能获得一份完整的文件拷贝。