BT协议分析文档newWord下载.docx
《BT协议分析文档newWord下载.docx》由会员分享,可在线阅读,更多相关《BT协议分析文档newWord下载.docx(16页珍藏版)》请在冰豆网上搜索。
使用支持该技术的BT下载软件,用户无需连上Tracker就可以下载,因为软件会在DHT网络中寻找下载同一文件的其他用户并与之通讯,开始下载任务。
有些软件(如比特精灵)还会自动通过DHT搜索种子资源,构成种子市场。
另外,这里使用的DHT算法叫Kademlia(在eMule中也有使用,称为KAD,具体实现协议有所不同)。
这种技术好处十分明显,就是大大减轻了Tracker的负担(甚至不需要)。
用户之间可以更快速建立通讯(特别是与Tracker连接不上的时候)。
2交互流程
为了部署BT,首先需要将一个扩展名为.torrent的文件放在一个普通的web服务器上。
.torrent文件包含了要共享的文件的信息,包括文件名、大小、文件的散列信息和一个指向tracker的url。
Tracker负责帮助下载者能够获取其它下载者的信息。
Tracker和下载者之间使用一种很简单的基于HTTP的协议进行交互,下载者告诉tracker自己要下载的文件、自己使用的端口以及类似的信息,tracker告诉下载者其它下载同样文件的下载者的联系信息。
下载者利用这些信息相互之间建立连接。
一个被成为“种子”的下载者,必须首先被启动,它知道完整的文件信息。
对tracker和web服务器的带宽需求很低,而种子必须至少发送原始文件的一份完整拷贝。
P2P的核心思想就是没有服务器的概念,任何一个下载者既是client,又是server。
下载者从别人那里取文件的时候,称为下载,而为别人提供文件的时候,称为上载(传)。
为了完成一次部署,至少需要一个tracker和一个seed。
所谓tracker,是一个服务器,负责帮助peers之间相互建立连接。
而seed,通常是第一个向tracker注册,然后它就开始进入循环,等待为别人提供文件,也就是说,第一个seed只负责上传文件。
一旦有一个peer向tracker注册后,就可以取得seed的信息,从而与seed建立连接。
从seed处读取文件。
由于原始的文件,只有seed拥有,所有说,seed至少要上传原始文件的一份完整拷贝。
如果又有一个peer加入进来,那么它可以同时和seed和前一个peer建立连接,然后从这两者处获取文件。
3协议详解
3.1.种子文件
种子文件(以.torrent结尾的文件)的内容是一个编码的字典,包含以下列表中的各项。
所有字符串值都以UTF-8编码。
标记没有为“可选”的键值是必需的字段:
表1种子文件内容列表
字段
属性
描述
info
必须
一个描述torrent文件的字典。
有两种可能的形式:
一种是没有目录结构的“单一文件”,另一种是包含子目录树的“多文件”
announce
服务器的发布URL(字符串)
announce-list
可选
发布列表,这是官方规范的一个扩展,它是向后兼容的。
此键值用来执行备份服务器的列表。
完整的规范可在找到。
creationdate
torrent文件的创建时间,使用标准Unix时间格式(从UTC1970年1月1日00:
00:
00开始,整数秒)
comment
创建torrent文件的名字和程序版本(字符串)
对于“单一文件”来说,信息字典包含以下的结构:
length:
文件字节数长度(整数)
md5sum:
(可选)一个32位的16进制字符串,它对应于文件的MD5和。
不被BitTorrent所使用,但被一些程序包含,以提供更大的兼容性。
name:
文件的名称。
建议使用(字节串)。
piecelength:
每个片断的字节数(整数)。
pieces:
包含所有20字节SHA-1散列值的字符串,每个片断都有唯一的值。
(字节串)
对于“多文件”来说,信息字典包含以下的结构:
结构中根目录的名称--包含上述文件列表中所有文件的目录(字符串)
files:
字典列表,每个文件都有一个。
每个在表中的字典包含以下内容:
length:
文件长度的字节数(整数)
(可选)一个32位的16进制字符串,它对应于文件的MD5和。
path:
一个包含着一个或多个字符串元素的,它包含路径和文件名。
每个表中元素对应于一个目录名或(在最后的元素的情况下)文件名。
例:
文件名“dir1/dir2/t”将包含三种串元素:
“dir1”、“dir2”和“t”。
编码为串表的例子“l4:
dir14:
dir28:
e”
需要注意的是,piecelength指定了标准的片断大小,通常是2的n次方。
片断长度的一般是根据torrent文件中所有数据的数量来决定的,如果片断太大,会导致效率低,出错概率增加;
而如果太小,则会使生成的torrent元数据文件过大。
常识决定使用最小的片断大小,这样就会使生成的torrent文件不大于50-75KB(可以减轻存储torrent文件服务器的负担)。
但是,由于没有严格限制存储和带宽,即使为了高效率的共享文件可能导致生成更大的torrent文件,也建议将小于8-10GB文件的片断大小设为小于或等于512KB。
通常大小是256KB,512KB和1MB。
除了最后的片断大小不定以外,其余片断大小是相等的。
因此片断的数目由总大小决定。
对于多文件模式下的片断边界,将文件数据设想为一个长的连续流,由文件有序列表中的每个文件相互连接而成。
片断数目和其边界的决定方式与单一文件相同。
片断可能由两个文件的边界组成。
还有,每个piece都有相应的SHA-1hash数据校验码。
这些校验码相互连接形成上述的信息字典的片断值(pieces)。
注意这不是一个表,而是一个字符串。
其长度必须是20字节的整数倍。
3.2.TrackerHTTP/HTTPS协议
Tracker是用来响应HTTPGET请求的一种HTTP/HTTPS服务。
该请求包括客户端的度量标准,这个标准可以帮助服务器全面统计torrent文件。
基本的URL包括元数据文件(torrent)中定义的“发布URL”。
再将那些参数通过标准CGI方法添加到此URL中(如:
“?
”在发布URL之后,紧接着“参数=值”的序列,分隔符“&
”)
注意所有在URL中的二进制数据(特别是info_hash和peer_id)必须使用转义符。
这意味着除0-9,a-z,A-Z和$-_.+!
*'
()外,其余字节需要采用“%nn”格式的编码,其中的“nn”是字节的16进制数值。
(详细见RFC1738)
3.2.1客户端向服务器的GET请求的参数如下:
客户端向服务器的GET请求的参数如下:
info_hash:
种子文件中20字节的SHA-1散列值。
注意此值会进入编码字典中,如上述的信息关键字的定义所述。
与不需编码的peer_id相比,它总是被URL编码。
peer_id:
客户端ID,客户端用来唯一标识自己ID的20字节的串,它在客户端启动时生成。
允许为任何值,包括二进制数据。
目前没有特定的算法来生成客户端ID。
但是,人们会认为它至少对于自己的本地机器是唯一的,从而应该像进程ID一样合并数据,也可能在启动时由时标记录。
见本区域下面的一般客户端编码的peer_id。
port(端口):
客户端监听的端口号。
BitTorrent所使用的典型端口是6881-6889。
如果此范围的端口都无效,可以选择其他的。
uploaded(已上传的):
从客户端发送“已开始”事件到服务器算起的上传总量,数值采用10进制的ASCII。
对于没有在官方规范明确指出的,该值应为已上传的字节总数。
downloaded(已下载的):
从客户端发送“已开始”事件到服务器算起的下载总量,数值采用10进制的ASCII。
对于没有在官方规范明确指出的,该值应为已下载的字节总数。
left(剩下的):
客户端需要下载的字节数,以10进制ASCII编码。
compact(紧密的):
客户端接受一个紧密的响应。
客户端列表由客户端串代替,此串中每个客户端都编码成6字节。
前4字节是主机名(以网络的字节顺序),后两个字节是端口号(同样以网络字节的顺序)。
event(事件):
如果被指定,则是started,completed,stopped,(已开始,已完成,已停止,具体含义如下)中的一个,或者为空(表示未指定)。
如果未指定,此请求为常规时间间隔中的一次交互。
Ø
started(已开始):
向服务器发送的第一个请求,必须包含开始值的事件关键字。
stopped(已停止):
如果客户端关机则须发送到服务器上。
completed(已完成):
完成下载时必须发送到服务器上。
但是,当客户端启动时下载完成度为100%(即:
做种中)则不会发送。
可能这是允许服务器增加“已完成下载”的方法。
ip:
可选。
客户端的真实IP地址,以点分四元组格式或RFC3513中定义的16进制IPv6地址。
注意:
大体上此参数没有客户端地址重要,它能由IP地址决定,HTTP请求也来自该处。
仅在请求参与的IP地址不是客户端的IP地址的情况下才需要。
这种情况发生在客户端通过代理服务器与服务器进行通信的情形。
当客户端和服务器同时处在本地NAT网关时也需要。
原因是服务器会发出客户端的内部地址(RFC1918),这是不可到达的。
所以客户端必须清楚地把自己的外部可到达的IP地址发送到其他客户端中。
不同的服务器对此参数的解释有所不同。
某些只有当请求参与的IP地址属于RFC1918时才允许。
有些无条件允许,但有些则完全忽略。
如果使用IPv6地址(如:
2001:
db8:
1:
2:
:
100),则表示客户端能通过IPv6进行通信。
numwant(需求数目):
客户端想从服务器接收的用户数目。
允许此值为“0”。
如果不用此项,则默认值为50个用户。
key关键字:
一个不与任何用户共享的另外的标识。
当IP地址改变后,允许客户端证明它们的标识。
trackerid:
如果先前发布包含tracker的id,它应放在这里。
no_peer_id(可选):
其值为1,表示不需要其它用户的peer_id信息。
一个实际的数据包如下图所示:
图1客户端对Tracker的请求
上图是HTTP协议的负载部分,其中的URL中包含着我们前面介绍过的各字段及其对应信息。
该URL为
/announce?
info_hash=%ca%2b%5cNT%f6%b0%c9%00%c7%0bq%9d%29A4%f3%bc%99%cb&
peer_id=M6-0-0--%8b%92%22%dag%8a%8a%96%aa%16%c9h&
port=53779&
uploaded=0&
downloaded=0&
left=100887162&
key=A8FD6ACC&
numwant=200&
compact=1&
no_peer_id=1
所以,对应的其“info_hash=”之后的字符串为前面提到的种子文件中20字节的SHA-1散列值。
但是这里这个值是经过编码的,比如%ca%2b%5c代表16进制0xca2b5c。
“&
”是分隔符,之后的peer_id=M6-0-0--%8b%92%22%dag%8a%8a%96%aa%16%c9h即为客户端用来唯一标识自己ID的20字节的串,它采用了同样的编码。
我们还可以得到
port=53779:
端口是53779
uploaded=0:
上传了0字节
downloaded=0:
下载了0字节
left=100887162:
还剩下100887162字节
key=A8FD6ACC:
客户端标识为A8FD6ACC
numwant=200:
客户端想从服务器接收的用户数目为200
compact=1:
客户端接受一个紧密的响应
no_peer_id=1:
不需要其它用户的peer_id信息
3.2.2tracker的响应
服务器作出“text/plain”文档的响应包括以下编码字典的关键字:
failurereason(失败原因):
如果当前使用此值,则其余关键字不会使用。
该值是可读的错误消息,包括请求失败的原因。
(字符串)
warningmessage(警告消息):
(新,可选)与失败原因相似,但响应仍然会被正常处理。
警告消息看起来像错误。
interval(时间间隔):
以秒计算,是客户端发送定期请求到服务器之后等待的时间。
(强制)
mininterval(最小时间间隔):
最小发布时间间隔。
当前客户重发间隔不能小于此值。
trackerid:
一个客户端应在下一个通告发回的字符串。
如果没有该值,先前通告会发出一个trackerid,不要丢弃旧的值,一直使用它。
complete(完成):
拥有完整文件的用户数,即做种者(整数)
incomplete(未完成):
非种子用户的数目,也叫“吸血者”(整数)
注:
以上2项有时候为
donepeers:
下载完毕的节点个数
numpeers:
当前下载的节点个数
peers(用户):
这个字段在字典模式(dictionarymodel)下是字典的列表,每个值都有如下的关键字:
peerid(用户id):
用户的自选择ID,如上述用来发送服务器请求的(字符串)
ip:
用户的IP地址(IPv4或IPv6格式)或域名(字符串)
port(端口):
用户的端口号(整数)
peers在二进制模式(binarymodel)下,其值是一个6个字节的串,其中前4个字节是ip,后2个字节是端口号(都采用网络字节序也就是大字节序)。
注意此响应有三层字典嵌套。
例如:
d5:
其中“....................”是20字节的info_hash,以上表明有5个做种者,10个吸血者和50个完成下载的用户。
一个真实的数据包如下所示
图2Tracker响应
上图中,“10:
donepeersi5e”表示5个完成用户;
“8:
intervali1800e”表示间隔时间是1800秒;
“9:
numpeersi6e”表示有6个peer;
“5:
peers36:
…...”表示用户的ip和端口信息,注意到6个字节表示一个用户的信息,6个用户,6*6正好是36个字节。
如上所述,用户(peers)列表长度默认值为50。
如果连接的用户少于该值,列表会更小。
另外,服务器随机选择用户及其响应。
服务器在响应请求时可能使用一个更智能的机构来选择用户。
例如,应避免向其他做种者报告种子。
在事件发生(即:
已停止或已完成)或客户端需要连接更多的用户时,客户端向服务器发送请求的间隔可以低于指定的时间间隔。
但是,为了获得更多的用户而向服务器频繁地请求会被认为是错误的行为。
如果客户端想在回应中得到许多用户,则需要在“需求数目”参数中设定。
使用者注意:
30个用户就算丰富的源了,官方客户端版本3(v3)实际上在连接数少于30时会尝试增加新的连接,当连接数大于或等于55时会拒绝连接多余的用户。
这个值对性能很重要。
当完成下载一个新的片断时,“已拥有”消息(见下面)将会发送到最活动的用户。
结果广播通信量与用户数目成正比例增加。
大于25时,新用户不太可能会增加下载速度。
有人强烈建议用户界面设计者使该项模糊和很难修改,因为那样做几乎没有用。
3.2.3服务器“scrape”约定
根据惯例,多数服务器支持请求的另一种形式,这种方式询问给定的服务器正在处理的torrent(或所有的torrent)。
通常叫做“刮页”("
scrapepage"
),因为它自动处理“刮屏”("
screenscraping"
,服务器统计页)冗长的部分。
刮URL也是一种类似于上面描述的HTTPGET方法。
但基本URL不同。
用以下步骤来得到刮URL:
从发布URL开始寻找其中最后一个“/”。
如果在文本之后的“/”不是“announce”,它将被作为一个符号,此符号不支持刮约定。
如果是,则以“scrape”代替“announce”来找到刮页。
(发布URL->
scrape后的URL)
->
(不支持scrape)
此“scrape”标准是由Bram在BitTorrent开发列表文件中说明的。
具体见
可选参数“info_hash”可以作为scrape后的URL的一个补充,是一个20字节的值。
为了降低服务器的负载和带宽,强烈建议软件作者尽可能使用“info_hash”参数。
3.3.Peerwireprotocol(用户线路协议)
用户线路协议使种子文件中片断(pieces)的交换变得更容易。
注意原始规范在描述用户协议时也使用术语“片断”(piece),但与种子文件中的术语“片断”(piece)不同。
由于该原因,在本文档中将用术语“块”(block)来描述用户之间通过线路交换的数据。
客户端必须为每个远程用户的连接保持状态信息:
choked(被阻塞):
远程用户是否阻塞此客户端。
当用户阻塞客户端时,不会响应任何请求。
客户端不应尝试发送请求块,所有未响应的请求会被远程用户丢弃。
interested(感兴趣):
远程用户是否对此客户端感兴趣。
当客户端未阻塞时,远程用户将开始发送请求块。
注意这也意味着客户端需要记住自己是否对远程用户感兴趣和阻塞它。
因此,真正的列表看起来像这样:
am_choking:
此客户端阻塞远程用户
am_interseted:
此客户端对远程用户感兴趣
peer_chokong:
远程用户阻塞此客户端
peer_interested:
远程用户对此客户端感兴趣
客户端的连接以“被阻塞”和“不感兴趣”开始。
也就是:
am_choking=1
am_interested=0
peer_choking=1
peer_interested=0
当客户端对远程用户感兴趣并且远程用户未阻塞该客户端时,客户端开始下载块。
当客户端没有阻塞远程用户并且远程用户对该客户端感兴趣时,客户端开始上传块。
客户端保持通知远程用户自己是否对它们感兴趣,这是很重要的。
与每个远程用户连接的状态信息应保持最新,直到该客户端被阻塞。
这允许远程用户知道当自己未阻塞时,客户端是否会开始下载;
反之亦然。
3.3.1数据类型
如果不特别指定,在用户线路协议中的所有整数都会编码成4字节bigendian(大字节序)值,包括在握手之后所有消息中的长前缀,。
3.3.2消息流
用户线路协议包括初始握手。
之后,用户通过带有长度前缀消息的交换来进行通信。
长度前缀是上述的一个整数。
3.3.3握手消息
握手是必需的消息,它一定是由客户端发送的第一条消息,长度是(49+len(pstr))bytes。
它的具体格式如下
handshake:
:
<
pstrlen>
pstr>
reserved>
info_hash>
peer_id>
一个真实的数据包如下图所示
图3handshake消息
其中,各个个字段的含义为:
pstrlen:
串的长度,作为单个原始字节
pstr:
协议的串标识符
reserved:
8个保留字节。
当前的执行使用全0。
字节中的每位可以用来改变协议的行为。
一封Bram的电子邮件建议首先使用末位,以使首位可用来改变末位的含义。
种子文