1、BT协议分析文档newBitTorrent协议分析文档1 协议相关概念1.1 简介BitTorrent协议(简称BT,俗称比特洪流、BT下载)是一个网络文件传输协议,它能够实现点对点文件分享的技术。比起其他点对点的协议,它更有多点对多点的特性,这个特点简单的说就是:下载的人越多,速度越快。下载完不去马上关闭BitTorrent软件,就可以成为种子(拥有完整的文件者)分享让其他人下载。1.2 原理普通的HTTP/FTP下载使用TCP/IP协议,BitTorrent协议是架构于TCP/IP协议之上的一个P2P文件传输协议,处于TCP/IP结构的应用层。 BitTorrent协议本身也包含了很多具体
2、的内容协议和扩展协议,并在不断扩充中。根据BitTorrent协议,文件发布者会根据要发布的文件生成提供一个.torrent文件,即种子文件,也简称为“种子”。.torrent文件本质上是文本文件,包含Tracker信息和文件信息两部分。Tracker信息主要是BT下载中需要用到的Tracker服务器的地址和针对Tracker服务器的设置,文件信息是根据对目标文件的计算生成的,计算结果根据BitTorrent协议内的B编码规则进行编码。它的主要原理是需要把提供下载的文件虚拟分成大小相等的块,块大小必须为2k的整数次方(由于是虚拟分块,硬盘上并不产生各个块文件),并把每个块的索引信息和Hash验
3、证码写入.torrent文件中;所以,.torrent文件就是被下载文件的“索引”。下载者要下载文件内容,需要先得到相应的.torrent文件,然后使用BT客户端软件进行下载。下载时,BT客户端首先解析.torrent文件得到Tracker地址,然后连接Tracker服务器。Tracker服务器回应下载者的请求, 提供下载者其他下载者(包括发布者)的IP。下载者再连接其他下载者,根据.torrent文件,两者分别对方告知自己已经有的块,然后交换对方没有的数 据。此时不需要其他服务器参与,分散了单个线路上的数据流量,因此减轻了服务器负担。下载者每得到一个块,需要算出下载块的Hash验证码与.to
4、rrent文件中的对比,如果一样则说明块正确,不一样则需要重新下载这个块。这种规定是为了解决下载内容准确性的问题。一般的HTTP/FTP下载,发布文件仅在某个或某几个服务器,下载的人太多,服务器的带宽很易不胜负荷,变得很慢。而BitTorrent协议下载的特点是,下载的人越多,提供的带宽也越多,种子也会越来越多,下载速度就越快。而有些人下载完成后关掉下载任务,提供较少量数据给其他用户,为尽量避免这种行为,在非官方BitTorrent协议中存在超级种子(super seed)的算法。这种算法允许文件发布者分几步发布文件,发布者不需要一次提供文件所有内容,而是慢慢开放的下载内容的比例,延长下载时间
5、。此时,速度快的人由于未下载完必须提供给他人数据,速度慢的人有更多机会得到数据。1.3 相关概念Tracker:收集下载者信息的服务器,并将此信息提供给其他下载者,使下载者们相互连接起来,传输数据。种子:指一个下载任务中所有文件都被某下载者完整的下载,此时下载者成为一个种子。发布者本身发布的文件就是原始种子。也指.torrent文件。做种:发布者提供下载任务的全部内容的行为;下载者下载完成后继续提供给他人下载的行为。DHT网络:DHT全称为分布式哈希表(Distributed Hash Table),是一种分布式存储方法。在不需要服务器的情况下,每个客户端负责一个小范围的路由,并负责存储一小部
6、分数据,从而实现整个DHT网络的寻址和存储。使用支持该技术的BT下载软件,用户无需连上Tracker就可以下载,因为软件会在DHT网络中寻找下载同一文件的其他用户并与之通讯,开始下载任务。有些软件(如比特精灵)还会自动通过DHT搜索种子资源,构成种子市场。另外,这里使用的DHT算法叫Kademlia(在eMule中也有使用,称为KAD,具体实现协议有所不同)。这种技术好处十分明显,就是大大减轻了Tracker的负担(甚至不需要)。用户之间可以更快速建立通讯(特别是与Tracker连接不上的时候)。2 交互流程为了部署 BT,首先需要将一个扩展名为 .torrent 的文件放在一个普通的web服
7、务器上。.torrent文件包含了要共享的文件的信息,包括文件名、大小、文件的散列信息和一个指向tracker的 url。Tracker负责帮助下载者能够获取其它下载者的信息。Tracker和下载者之间使用一种很简单的基于HTTP的协议进行交互,下载者告诉 tracker自己要下载的文件、自己使用的端口以及类似的信息,tracker告诉下载者其它下载同样文件的下载者的联系信息。下载者利用这些信息相互之间建立连接。一个被成为“种子”的下载者,必须首先被启动,它知道完整的文件信息。对tracker和web服务器的带宽需求很低,而种子必须至少发送原始文件的一份完整拷贝。P2P的核心思想就是没有服务器
8、的概念,任何一个下载者既是client,又是server。下载者从别人那里取文件的时候,称为下载,而为别人提供文件的时候,称为上载(传)。为了完成一次部署,至少需要一个 tracker和一个seed。所谓tracker,是一个服务器,负责帮助peers之间相互建立连接。而seed,通常是第一个向tracker注册,然后它就开始进入循环,等待为别人提供文件,也就是说,第一个seed只负责上传文件。一旦有一个peer向tracker注册后,就可以取得 seed的信息,从而与seed建立连接。从seed处读取文件。由于原始的文件,只有seed拥有,所有说,seed至少要上传原始文件的一份完整拷贝。如
9、果又有一个peer加入进来,那么它可以同时和seed和前一个peer建立连接,然后从这两者处获取文件。3 协议详解3.1. 种子文件种子文件(以 .torrent 结尾的文件)的内容是一个编码的字典,包含以下列表中的各项。所有字符串值都以 UTF-8 编码。标记没有为“可选”的键值是必需的字段: 表1 种子文件内容列表字段属性描述info必须一个描述 torrent 文件的字典。有两种可能的形式:一种是没有目录结构的“单一文件”,另一种是包含子目录树的“多文件”announce必须服务器的发布URL (字符串)announce-list可选发布列表,这是官方规范的一个扩展,它是向后兼容的。此键
10、值用来执行备份服务器的列表。完整的规范可在 找到。creation date可选torrent 文件的创建时间,使用标准 Unix 时间格式(从 UTC 1970年1月1日 00:00:00 开始,整数秒)comment可选创建 torrent 文件的名字和程序版本(字符串)对于“单一文件”来说,信息字典包含以下的结构:length: 文件字节数长度(整数)md5sum: (可选)一个 32 位的 16 进制字符串,它对应于文件的 MD5和。不被 BitTorrent 所使用,但被一些程序包含,以提供更大的兼容性。name: 文件的名称。建议使用(字节串)。piece length: 每个片断
11、的字节数(整数)。pieces: 包含所有 20 字节 SHA-1 散列值的字符串,每个片断都有唯一的值。(字节串)对于“多文件”来说,信息字典包含以下的结构:name: 结构中根目录的名称包含上述文件列表中所有文件的目录(字符串)piece length: 每个片断的字节数(整数)。pieces: 包含所有 20 字节 SHA-1 散列值的字符串,每个片断都有唯一的值。(字节串)files: 字典列表,每个文件都有一个。每个在表中的字典包含以下内容:length:文件长度的字节数(整数)md5sum:(可选)一个 32 位的 16 进制字符串,它对应于文件的 MD5和。不被 BitTorre
12、nt 所使用,但被一些程序包含,以提供更大的兼容性。path:一个包含着一个或多个字符串元素的,它包含路径和文件名。每个表中元素对应于一个目录名或(在最后的元素的情况下)文件名。例:文件名“dir1/dir2/t”将包含三种串元素:“dir1”、“dir2”和“t”。编码为串表的例子“l4:dir14:dir28:e”需要注意的是,piece length指定了标准的片断大小,通常是 2 的 n 次方。片断长度的一般是根据 torrent 文件中所有数据的数量来决定的,如果片断太大,会导致效率低,出错概率增加;而如果太小,则会使生成的 torrent 元数据文件过大。常识决定使用最小的片断大小
13、,这样就会使生成的 torrent 文件不大于 5075 KB (可以减轻存储 torrent 文件服务器的负担)。但是,由于没有严格限制存储和带宽,即使为了高效率的共享文件可能导致生成更大的 torrent 文件,也建议将小于 810 GB 文件的片断大小设为小于或等于 512 KB。通常大小是 256 KB,512 KB 和 1 MB。除了最后的片断大小不定以外,其余片断大小是相等的。因此片断的数目由总大小决定。对于多文件模式下的片断边界,将文件数据设想为一个长的连续流, 由文件有序列表中的每个文件相互连接而成。片断数目和其边界的决定方式与单一文件相同。片断可能由两个文件的边界组成。还有,
14、每个piece都有相应的 SHA-1 hash 数据校验码。这些校验码相互连接形成上述的信息字典的片断值(pieces)。注意这不是一个表,而是一个字符串。其长度必须是 20 字节的整数倍。3.2. Tracker HTTP/HTTPS 协议Tracker是用来响应 HTTP GET 请求的一种 HTTP/HTTPS 服务。该请求包括客户端的度量标准,这个标准可以帮助服务器全面统计 torrent 文件。基本的 URL 包括元数据文件(torrent)中定义的“发布 URL”。再将那些参数通过标准 CGI 方法添加到此 URL 中(如:“?”在发布 URL 之后,紧接着“参数=值”的序列,分隔
15、符“&”)注意所有在 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 ,客户端用来唯一标识
16、自己 ID 的 20 字节的串,它在客户端启动时生成。允许为任何值,包括二进制数据。目前没有特定的算法来生成客户端 ID。但是,人们会认为它至少对于自己的本地机器是唯一的,从而应该像进程 ID 一样合并数据,也可能在启动时由时标记录。见本区域下面的一般客户端编码的 peer_id。port(端口):客户端监听的端口号。BitTorrent 所使用的典型端口是 6881-6889。如果此范围的端口都无效,可以选择其他的。uploaded(已上传的):从客户端发送“已开始”事件到服务器算起的上传总量,数值采用 10 进制的 ASCII。对于没有在官方规范明确指出的,该值应为已上传的字节总数。dow
17、nloaded(已下载的):从客户端发送“已开始”事件到服务器算起的下载总量,数值采用 10 进制的 ASCII。对于没有在官方规范明确指出的,该值应为已下载的字节总数。left(剩下的):客户端需要下载的字节数,以 10 进制 ASCII 编码。compact(紧密的):客户端接受一个紧密的响应。客户端列表由客户端串代替,此串中每个客户端都编码成 6 字节。前 4 字节是主机名(以网络的字节顺序),后两个字节是端口号(同样以网络字节的顺序)。event(事件):如果被指定,则是started, completed, stopped,(已开始,已完成,已停止,具体含义如下)中的一个,或者为空(
18、表示未指定)。如果未指定,此请求为常规时间间隔中的一次交互。 started(已开始):向服务器发送的第一个请求,必须包含开始值的事件关键字。 stopped(已停止):如果客户端关机则须发送到服务器上。 completed(已完成):完成下载时必须发送到服务器上。但是,当客户端启动时下载完成度为 100% (即:做种中)则不会发送。可能这是允许服务器增加“已完成下载”的方法。ip:可选。客户端的真实 IP 地址,以点分四元组格式或 RFC3513 中定义的 16 进制 IPv6 地址。注意:大体上此参数没有客户端地址重要,它能由 IP 地址决定,HTTP 请求也来自该处。仅在请求参与的 IP
19、 地址不是客户端的 IP 地址的情况下才需要。这种情况发生在客户端通过代理服务器与服务器进行通信的情形。当客户端和服务器同时处在本地 NAT 网关时也需要。原因是服务器会发出客户端的内部地址(RFC1918),这是不可到达的。所以客户端必须清楚地把自己的外部可到达的 IP 地址发送到其他客户端中。不同的服务器对此参数的解释有所不同。某些只有当请求参与的 IP 地址属于 RFC1918 时才允许。有些无条件允许,但有些则完全忽略。如果使用 IPv6 地址(如:2001:db8:1:2:100),则表示客户端能通过 IPv6 进行通信。numwant(需求数目):可选。客户端想从服务器接收的用户数
20、目。允许此值为“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%2
21、9A4%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
22、%96%aa%16%c9h即为客户端用来唯一标识自己 ID 的 20 字节的串,它采用了同样的编码。我们还可以得到port=53779 :端口是53779uploaded=0 :上传了0字节downloaded=0 :下载了0字节left=100887162 :还剩下100887162字节key=A8FD6ACC :客户端标识为A8FD6ACCnumwant=200 :客户端想从服务器接收的用户数目为200compact=1 :客户端接受一个紧密的响应no_peer_id=1 :不需要其它用户的peer_id信息3.2.2 tracker的响应服务器作出“text/plain”文档的响应包括以
23、下编码字典的关键字:failure reason(失败原因):如果当前使用此值,则其余关键字不会使用。该值是可读的错误消息,包括请求失败的原因。(字符串)warning message(警告消息):(新,可选)与失败原因相似,但响应仍然会被正常处理。警告消息看起来像错误。interval(时间间隔):以秒计算,是客户端发送定期请求到服务器之后等待的时间。(强制)min interval(最小时间间隔):最小发布时间间隔。当前客户重发间隔不能小于此值。tracker id:一个客户端应在下一个通告发回的字符串。如果没有该值,先前通告会发出一个trackerid,不要丢弃旧的值,一直使用它。com
24、plete(完成):拥有完整文件的用户数,即做种者(整数)incomplete(未完成):非种子用户的数目,也叫“吸血者”(整数)注:以上2项有时候为done peers :下载完毕的节点个数num peers :当前下载的节点个数peers(用户):这个字段在字典模式(dictionary model)下是字典的列表,每个值都有如下的关键字: peer id(用户 id):用户的自选择 ID,如上述用来发送服务器请求的(字符串) ip:用户的 IP 地址(IPv4 或 IPv6 格式)或域名(字符串) port(端口):用户的端口号(整数)peers在二进制模式(binary model)下
25、,其值是一个6个字节的串,其中前4个字节是ip,后2个字节是端口号(都采用网络字节序也就是大字节序)。注意此响应有三层字典嵌套。例如:d5:其中“.”是 20 字节的 info_hash,以上表明有 5 个做种者,10 个吸血者和 50 个完成下载的用户。一个真实的数据包如下所示图 2 Tracker 响应上图中,“10:done peersi5e”表示5个完成用户;“8:intervali1800e”表示间隔时间是1800秒;“9:num peersi6e”表示有6个peer;“5:peers36:.”表示用户的ip和端口信息,注意到6个字节表示一个用户的信息,6个用户,6*6正好是36个字
26、节。如上所述,用户(peers)列表长度默认值为 50。如果连接的用户少于该值,列表会更小。另外,服务器随机选择用户及其响应。服务器在响应请求时可能使用一个更智能的机构来选择用户。例如,应避免向其他做种者报告种子。在事件发生(即:已停止或已完成)或客户端需要连接更多的用户时,客户端向服务器发送请求的间隔可以低于指定的时间间隔。但是,为了获得更多的用户而向服务器频繁地请求会被认为是错误的行为。如果客户端想在回应中得到许多用户,则需要在“需求数目”参数中设定。使用者注意:30 个用户就算丰富的源了,官方客户端版本 3(v3)实际上在连接数少于 30 时会尝试增加新的连接,当连接数大于或等于 55
27、时会拒绝连接多余的用户。这个值对性能很重要。当完成下载一个新的片断时,“已拥有”消息(见下面)将会发送到最活动的用户。结果广播通信量与用户数目成 正比例增加。大于 25 时,新用户不太可能会增加下载速度。有人强烈建议用户界面设计者使该项模糊和很难修改,因为那样做几乎没有用。3.2.3 服务器“scrape”约定根据惯例,多数服务器支持请求的另一种形式,这种方式询问给定的服务器正在处理的 torrent (或所有的 torrent)。通常叫做“刮页”(scrape page),因为它自动处理“刮屏”( screen scraping,服务器统计页)冗长的部分。刮 URL 也是一种类似于上面描述的
28、 HTTP GET 方法。但基本 URL 不同。用以下步骤来得到刮 URL:从发布 URL 开始寻找其中最后一个“/”。如果在文本之后的“/”不是“announce”,它将被作为一个符号,此符号不支持刮约定。如果是,则以“scrape” 代替“announce”来找到刮页。例:(发布 URL - scrape后的 URL) - - - - (不支持scrape) - - (不支持scrape) - (不支持scrape)此“scrape”标准是由 Bram 在 BitTorrent 开发列表文件中说明的。具体见可选参数“info_hash”可以作为scrape后的URL的一个补充,是一个 20
29、 字节的值。为了降低服务器的负载和带宽,强烈建议软件作者尽可能使用“info_hash”参数。3.3. Peer wire protocol (用户线路协议)用户线路协议使种子文件中片断(pieces)的交换变得更容易。注意原始规范在描述用户协议时也使用术语“片断”(piece),但与种子文件中的术语“片断”(piece)不同。由于该原因,在本文档中将用术语“块”(block)来描述用户之间通过线路交换的数据。客户端必须为每个远程用户的连接保持状态信息:choked(被阻塞):远程用户是否阻塞此客户端。当用户阻塞客户端时,不会响应任何请求。客户端不应尝试发送请求块,所有未响应的请求会被远程用户
30、丢弃。interested(感兴趣): 远程用户是否对此客户端感兴趣。当客户端未阻塞时,远程用户将开始发送请求块。注意这也意味着客户端需要记住自己是否对远程用户感兴趣和阻塞它。因此,真正的列表看起来像这样:am_choking:此客户端阻塞远程用户am_interseted:此客户端对远程用户感兴趣peer_chokong:远程用户阻塞此客户端peer_interested:远程用户对此客户端感兴趣客户端的连接以“被阻塞”和“不感兴趣”开始。也就是:am_choking = 1am_interested = 0peer_choking = 1peer_interested = 0当客户端对远程
31、用户感兴趣并且远程用户未阻塞该客户端时,客户端开始下载块。当客户端没有阻塞远程用户并且远程用户对该客户端感兴趣时,客户端开始上传块。客户端保持通知远程用户自己是否对它们感兴趣,这是很重要的。与每个远程用户连接的状态信息应保持最新,直到该客户端被阻塞。这允许远程用户知道当自己未阻塞时,客户端是否会开始下载;反之亦然。3.3.1 数据类型如果不特别指定,在用户线路协议中的所有整数都会编码成 4 字节 big endian(大字节序) 值,包括在握手之后所有消息中的长前缀,。3.3.2 消息流用户线路协议包括初始握手。之后,用户通过带有长度前缀消息的交换来进行通信。长度前缀是上述的一个整数。3.3.3 握手消息握手是必需的消息,它一定是由客户端发送的第一条消息,长度是(49+len(pstr) bytes。它的具体格式如下handshake::一个真实的数据包如下图所示图 3 handshake消息其中,各个个字段的含义为:pstrlen:串的长度,作为单个原始字节pstr:协议的串标识符reserved:8 个保留字节。当前的执行使用全0。字节中的每位可以用来改变协议的行为。一封 Bram 的电子邮件建议首先使用末位,以使首位可用来改变末位的含义。info_hash:种子文
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1