BitTorrent协议规范

转载 2015年07月10日 09:39:10

转自:http://blog.chinaunix.net/uid-14408083-id-2814554.html


鉴定


BitTorrent是由布莱姆·科恩设计的一个端对端(peer to peer)文件共享协议,此协议使多个peers通过不可信任的网络的文件传输变得更容易。

目的


此规范的目的是详细介绍 BitTorrent 协议规范 v1.0 。Bram 的协议规范网站 http://www.bittorrent.com/protocol.html 简要地叙述了此协议,在部分范围缺少详细的行为阐述。该文档使用清楚明确的措辞书写,希望它能够成为一个正式的规范,当然也可用于将来实现和讨论的基础。

此文档旨在由 BitTorrent 开发社区维护和使用。欢迎大家为它做贡献,其中的内容仅代表当前协议,但它已经被目前许多存在的客户端实现所采用。

应用范围


本文档适用于 BitTorrent 协议规范的第一版(v1.0)。目前,这份文档应用于 torrent 文件结构规范、peer wire协议规范和Tracker HTTP/HTTPS 协议规范。如果某个协议有了新的修订,请到对应页面查看,而不是在这里。

(译者注:在本文档翻译过程中,如果遇到没有对应标准翻译的术语,一律不予翻译,例如torrent,peer,tracker等)

约定


为了简明和准确地表达信息,在本文档中,使用了许多约定。
  • peer v/s 客户端(client):在本文档中,一个peer可以是任何参与下载的 BitTorrent 客户端。客户端也是一个peer,尽管 BitTorrent 客户端运行在本地机器上。本规范的读者可能会认为自己是连接了许多peer的客户端。
  • 片(piece) v/s 块(block):在本文档中,片是指在元信息文件(metainfo file)中描述的一部分已下载的数据,它可通过 SHA-1 hash 来验证。而块是指客户端向peer请求的一部分数据。两块或更多块组成一个完整的可以被验证的片。
  • 实际标准(defacto standard):粗斜体文本指出一个规则在许多不同的BitTorrent客户端实现中如此通用,以致于该规则被当做是一个实际标准。

(译者注:peer一般译成“端”,所以p2p应该翻译成端对端,但这并没有一个标准的译法,因此在本文中不作翻译,同时译者应该将peer to peer和数据链路层的点对点协议(也缩写成p2p)区分开)

B编码(Bencoding)


B编码是一种以简洁的格式描述和组织数据的方法。支持下列类型:字节串、整数、lists和dictionaries。

字节串(byte strings)

字节串按如下方式编码:<以十进制ASCII编码的串长度>:<串数据>
注意:字节串编码没有开始和结束分隔符。

例:4:spam表示字节串“spam”

整数

整数按如下方式编码:i<以十进制ASCII编码的整数>e
开始的“i”与结尾的“e”分别是开始和结束分隔符。可以使用如“i-3e”之类的负数。但是你不能把“0”放到数字的前面,如“i04e”。另外,“i0e”是有效的。

例:“i3e”表示整数“3”
  • 注意:对于这个整数的最大位数规范并没有做出规定,(待译)

lists

lists按如下方式编码:le
开始的“l”(l是小写的L,而不是大写的i)与结尾的“e”分别是开始和结束分隔符。lists可以包含任何B编码的类型,包括整数、串、dictionaries和其他的lists。

例:l4:spam4:eggse 表示含有两个串的lists:[“spam”、“eggs”]

dictionaries

dictionaries按如下方式编码:de
开始的“d”与结尾的“e”分别是开始和结束分隔符。注意键(key)必须被B编码为串。值可以是任何B编码的类型,包括整数、串、lists和其他的dictionaries。键(key)必须是串,并且以排序的顺序出现(以原始串排列,而不是以字母数字顺序)。串采用二进制比较方式,而不是特定于某种文化的自然比较(即既不是按照中文的比较方式,也不是按照英文的排序方式)。

例1:d3:cow3:moo4:spam4:eggse 表示dictionary { "cow" => "moo", "spam" => "eggs" }

例2:d4:spaml1:a1:bee 表示dictionary { "spam" => ["a", "b"] }

例3:d9:publisher3:bob17:publisher-webpage15:www.example.com18:publisher.location4:homee表示dictionary  { "publisher" => "bob", "publisher-webpage" => "www.example.com", "publisher.location" => "home" }

(译者注:对于string和interger,目前已经存在官方的翻译,但list和dictionary并不存在一个统一的译法)

B编码实现

  • C
  • Perl
  • Java
  • Python by Hackeron
  • Decoding encoding bencoded data with haskell by Edi
  • Objective-C by Chrome
  • 元信息文件结构(Metainfo File Structure)


    元信息文件里面的所有数据都以B编码方式编码。

    元信息文件(就是平常咱们经常接触到的以.torrent为后缀的文件)的内容是一个B编码的dictionary,包含下面列出的键(key),其中字符串类型的值均以UTF-8编码。

    • info:该键(key)对应的值是一个描述torrent文件的dictionary。该dictionary有两种可能的结构:一种对应于没有目录结构的单文件torrent(即种子文件中只包含一个文件),另一种则对应于多文件torrent(详情请看下文)。(dictionary类型)
    • announce:改键对应的值是tracker的announce URL。(字符串类型)
    • announce-list:(可选),这是对正式规范的一个扩展,目的是提供向后兼容性。(list of lists of strings类型)
    • creation date:(可选),该键对应的值是torrent文件的创建时间,时间使用标准的UNIX时间格式。(整数类型,是从1970-01-01 00:00:00开始的秒数) 
    • comment:(可选),该键对应的值是torrent文件制作者的评论。(字符串类型)
    • create by:(可选),该键对应的值是制作torrent文件的程序的名称和版本。(字符串类型)
    • encoding:(可选),由上文可知,.torrent元文件中包含一个info dictionary,当该dictionary过大时,就需要对其分片(piece),该编码就是用来生成分片的。(字符串类型) 

    Info Dictionary(即键Info对应的值) 

    这一节主要是讲述两种模式(单文件模式和多文件)所共有的键(key)

    • piece length: 该键对应的值是每一片(piece)的字节数。(整数类型)
    • pieces: 该键对应的值由所有的20字节SHA1散列值连接而成的字符串,每一片(piece)均含有一个唯一的SHA1散列值。(字节串类型)
    • private: (可选),这个键(key)所对应值是整数类型,如果设置为1。客户端必须广播自己的存在,然后通过在元信息文件中显式描述的trackers得到其他的 peers,如果设置为0或者不设置,则表明客户端可以通过其他的方式来得到其他的peers,例如:PEX peer exchange技术,DHT技术等。“private”可以解释为没有外部的peer源(如果客户端不提供PEX peer exchange技术、DHT技术等,那么BitTorrent客户端必须通过tracker来得到其他的peers)。(整数类型)

    Info in Single File Mode(单文件模式下的Info键)

    对应于单文件模式,info dictionary包含如下的结构:

    • name:文件名. 建议使用。(字符串类型)
    • length:文件的所占字节数。(整数类型)
    • md5sum:(可选),相当于文件MD5和的32个字符的16进制串,BitTorrent根本就不使用这个键(key),但是有些程序为了更大的兼容性而包含它。(字符串类型)

    Info in Multiple File Mode(多文件模式下的Info键)

    对应于多文件模式,info dictionary包含如下的结构:

    • name:存储所有文件的目录名,建议使用。 (字符串类型)
    • files:a list of dictionaries,每一个文件对应一个dictionary(其实下面的字段刚好对应单文件模式下键(key),说得通俗点就是多文件模式是多个单文件模式的集合)。list下的每一个dictionary包含如下的结构:
      • length:参考单文件模式
      • md5sum:参考单文件模式
      • path:包含单个或多个元素的list,这些元素合成在一起表示文件路径和文件名。list中的每一个元素对应于一个目录名或者文件名(当是最后一个元素时对应文件名). 例如:文件"dir1/dir2/file.ext"由三个字符串元素组成:"dir1"、"dir2"和"file.ext"。这三个元素会被编码成B 编码的字符串list:l4:dir14:dir28:file.exte

    注意(Notes)

    • 键(key)piece length指出了片(piece)的正常大小,通常情况下2的n次方。一般根据torrent中文件数据的总大小来选择片(piece)大小,同时片(piece)太大导致低效,片(piece)太小,会使.torrent文件太大这两个因素也会影响片(piece)大小的选择。以前,选择的片(piece)大小,应该使.torrent文件不超过50-75KB(据推测这样能减轻存储.torrent文件的tracker的负载).
      • 目前最好的做法是保持片(piece)大小为512KB或者更少,虽然对于8-10GB的文件,会使.torrent文件过大,但是片数量的增加有利于文件的共享。最常用的片(piece)大小事256 kB,512 kB和1 MB。
      • 除了最后一块,所有的块都具有同样的大小,最后一块的大小是不规则的。这样片(piece)的数量就由( total length / piece size )决定。
      • 对于多文件模式情况下的片边界,可以将文件数据当做是一个长的连续流,这个流由文件列表中的文件串连而成。这样多文件模式下片(piece)数量的决定方式和单文件模式下就一样了。片(piece)有可能跨越文件边界。
    • 每个片(piece)都含有一个对应于该片(piece)数据的SHA1散列值。这些散列值串连起来就形成了上述info dictionary中pieces键所对应的值(key-value)。需要注意的是这个值并不是一个由字符串组成的list,而是一个字符串,字符串的长度是20的倍数。

    Tracker HTTP/HTTPS Protocol



    Tracker是一个响应HTTP GET请求的HTTP/HTTPS服务。这个请求包含来自客户端的度量信息,这些信息能够帮助Tracker全面地统计torrent。Tracker的响应包含一个peers列表,这个列表能够帮助客户端加入到torrent中。Base URL由元数据文件(即以.torrent为后缀的文件)中定义的announce URL组成,然后使用标准的CGI方式将这些请求参数追加到这个URL后面(CGI方式即在announce URL后面紧跟一个’?’,然后是一个以’&’分隔的’param=value’序列)。

     

    注意:URL中的所有二进制数据(特别是info_hash和peer_id)必须适当地进行转义。这意味着不在集合{0-9, a-z, A-Z, ‘.’, ‘-’, ‘_’, ‘~’}中的字节必须以’%nn’方式编码,其中nn是这个字节的十六进制值。

     

    对于一个20字节的散列值 “\x12\x34\x56\x78\x9a\xbc\xde\xf1\x23\x45\x67\x89\xab\xcd\xef\

    x12\x34\x56\x78\x9a”,其正确的编码形式是“%124Vx%9A%BC%DE%F1%23Eg%89%AB%CD%EF

    %124Vx%9A“。

    Tracker Request Parameters

    从客户端发送到Tracker的请求包含如下参数:

    • info_hash:URL编码的20字节SHA1散列,这个散列是元信息文件中info键所对应的值的SHA1散列。注意info键所对应的值是一个B编码的dictionary。
    • peer_id:使用URL编码的20字节串,用于标识客户端的唯一ID,由客户端启动时生成。这个ID可以是任意值,甚至可能是二进制数据。目前没有生成该ID的标准准则,尽管如此,实现者必须保证该ID对于本地主机是唯一的,因此可以通过包含进程ID和启动时记录的时间戳(timestamp)来达到这个目的。
    • port:客户端正在监听的端口号。为BitTorrent协议保留的端口号是6881-6889。如果不能使用该区间的数字建立端口号,客户端有可能拒绝建立连接。
    • uploaded:客户端已经上传的总量(从客户端发送’started’事件到Tracker算起),以十进制ASCII表示。尽管官方规范没有显式指定,大部分情况下是指已经下载的字节总数。
    • downloaded:已下载的字节总量(从客户端发送’started’事件到Tracker算起),以十进制ASCII表示。尽管官方规范没有显式指定,大部分情况下是指已经下载的字节总数。
    • left:客户端还没有下载的字节数,以十进制ASCII表示。
    • compact:如果设置为1,表示客户端接收压缩的响应包。这时peers列表将被peers串代替,在这个peers串中,每个peer占六个字节。这六个字节的前四个字节表示主机信息(以大端表示即以网络字节序),后两个字节表示端口号(以大端表示即以网络字节序)。需要注意的是,为了节约带宽,有些Tracker只支持返回压缩的响应包,如果没有将compact设置为1,Tracker将拒绝这个请求或者如果请求中不包括compact=0这个参数,Tracker将返回压缩的响应包。
    • no_peer_id:表示Tracker将省略peers dictionary中的id字段。如果启用compact,那么就会忽略这个选项。
    • event:如果指定的话,必须是started, completed, stopped和空(和不指定的意义一样)中的一个。如果一个请求不指定event,表明它只是每隔一定间隔发送的请求。

      started:第一个发送到Tracker的请求其event值必须是该值。

      stopped:如果正常关闭客户端,必须发送改事件到Tracker

      completed:如果下载完毕,必须发送改事件到Tracker。如果客户端启动之前,已经下载完成的话,则没有必要发送该事件。Tracker仅仅基于该事件增加已经完成的下载数。

    • ip:(可选),客户主机的IP地址,点分十进制IPv4或者RFC3513指定的十六进制Ipv6地址。注意:一般情况下这个参数没有必要,因为IP地址可以从HTTP请求中得到。只有当请求中的IP地址不是客户端的IP地址时,才需要这个参数(只有客户端通过代理和tracker 交互时才会发生这种情况)。如果客户端和Tracker都在NAT网关的同一侧,这个时候这个参数就是必要的。原因是Tracker会公布不能路由的客户端物理地址。因此客户端必须显示指出自己的IP地址,以将信息公布给外部的peers。不同的Tracker对待该参数的方式不同。一些Tracker只有当请求中的IP地址在RFC1918空间才使用它,有一些则无条件使用,另一些则根本不使用。如果是Ipv6地址,它表示这个客户端只能通过IPv6交互。
    • numwant:(可选),客户端希望从Tracker接受到的peers数,如果省略,则默认是50个。
    • key:(可选),不和其他用户共享的附加标识。当客户端IP地址改变时,可以使用该标识来证明自己的身份。
    • trackerid:(可选),如果之前的announce包含一个tracker id,那么当前的请求必须设置该参数。

    Tracker Response

    Tracker以”text/plain”文本响应客户端的请求,这个响应文本由B编码的dictionary组成,而这个dictionary则包含如下的键(key):

    • failure reason:如果dictionary中包含这个键(key),那么其他的键(key)就不会出现,该键(key)对应的值是一个可读的错误消息,它告诉用户的请求为什么会失败。(字符串类型)
    • warning message:(新加入,可选) 类似于failure reason,但是即使存在这个键,这个响应还会正常地处理下去。warning message显示为一个错误。(字符串类型)
    • interval:客户端每隔一定间隔就会向Tracker发送一个请求,这个键(key)以秒为单位指出这个间隔的大小。(整数类型)
    • min interval:(可选),最小的请求间隔,它表示客户端不能在这个时间间隔之内向Tracker重发请求。 (整数类型)
    • tracker id:客户端发送其下一个请求时必须返回给Tracker的一个字符串。如果缺失,但是上一个请求发送了tracker id,那么不要丢弃旧值,重复利用即可。
    • complete:完成整个文件下载的peers数,即做种者的数量。 (整数类型)
    • incomplete:非做种的peers数(还没有完成该文件下载的peers数),即“占他人便宜者”数。(整数类型)
    • peers:(dictionary model) 该键(key)对应的值是一个dictionary list(列表),该list中的每一个dictionary都包含如下的键(key):

      peer id:peer自己选择的用来标识自己的ID,上文在描述Tracker请求时已经说明。 (字符串类型)

      ip:peer的IP地址,可以是Ipv6/Ipv4/DNS name。(字符串类型)

      port:peer的端口号。(整数类型)

    • peers:(binary model) 不是使用上面描述的dictionary model,binary model下,该键(key)对应的值可以有多个六字节组成的字符串。这六个字节中的前四个是IP地址,后两个是端口号。IP地址和端口号均以网络字节序(大端)表示。

    综上所述,默认情况下peers列表的长度是50,如果torrent中的peers比较少,那么这个列表长度会更短。否则如果torrent中的peers比较多,那么tracker就得从这些peers中随机选择一些放入响应中。当响应请求时,Tracker需要使用一个智能的机制来进行peer选择。例如:向做种者报告种子就得避免(意思说如果没有好的机制,就有可能出现这种情况)。

     

    如果有一个事件发生(即stopped或者completed)或者客户端需要获得更多的peers时,客户端发送请求的频率肯定高于指定的间隔。尽管如此,不断地向Tracker发送请求以获得更多的peers绝对不是一个好主意。如果一个客户端想在其响应中包含一个更长的peer列表,那么它应该通过指定numwant参数来达到目的。

     

    BitTorrent实现者注意:30个peers已经是一个很大的数目了,官方的客户端版本3在peers少于30的情况,会积极地向Tracker建立连接已获得更多的peers,但是如果客户端已经有55个peers的情况下,客户端将拒绝和Tracker建立连接。这个值的选择对于性能非常重要。当一个片(piece)已经下载完成后,客户端需要将HAVE消息(参考下文)发送给大多数活跃的peers。结果是广播通信的代价与peers数量成正比。这个数高于25之后,新加入的peers不太可能提升下载速度。强烈建议UI设计者使这个数模糊和很难修改,因为这样做没有任何作用。


    Tracker 'scrape' Convention




    按照惯例:大部分Tracker支持另一种形式的请求,这种方式查询Tracker正在管理的给定torrent(或者所有的torrent)。这种方式通常被称为”scrape page”,因为这种方式会自动进行另一个单调乏味的流程:”screen scraping” Tracker的统计页。

     

    类似于上面描述的URL,scrape URL也是一个HTTP GET方法。尽管如此,它们的Base URL是不相同的。想要得到scrape URL,请使用如下步骤:从announce URL开始,找到该announce URL中的最后一个’/’,如果该’/’之后的内容不是’announce’,那么说明这个Tracker不支持scrape。如果这个Tracker支持scrape,那么使用’scrape’代替’announce’就可以得到scrape页。

     

    例:(announce URL -> scrape URL)

      ~http://example.com/announce         ->  ~http://example.com/scrape

      ~http://example.com/x/announce        ->  ~http://example.com/x/scrape

      ~http://example.com/announce.php      ->  ~http://example.com/scrape.php

      ~http://example.com/a                 ->  (scrape not supported)

      ~http://example.com/announce?x2%0644  ->  ~http://example.com/scrape?x2%0644

      ~http://example.com/announce?x=2/4     ->  (scrape not supported)

      ~http://example.com/x%064announce     ->  (scrape not supported)

     

    可用info_hash(上文描述的一个20字节值)作为scrape URL的补充。这样就限制了Tracker发送某个特定(info_hash限定)的torrent的数据。否则tracker正在管理的所有torrent的统计数据将会返回。强烈建议软件编写者尽可能使用info_hash参数,这样就能够减少Tracker的负载和带宽。

    你也可以指定多个info_hash参数给Tracker (得支持多个info_hash参数)。这不是官方规范的一部分,但是已经成为了实际标准,例如:

    http://example.com/scrape.php?info_hash=aaaaaaaaaaaaaaaaaaaa&info_hash=bbbbbbbbbbbbbbbbbbbb&info_hash=cccccccccccccccccccc

     

    这个HTTP GET方法的响应是一个’text/plain’或者有时候是用gzip压缩的文本,这个文本由一个B编码的dictionary组成,这个dictionary包含如下的键(key):

    • files:这是一个B编码的dictionary,在该dictionary中,每一个torrent文件都有其相应的键/值,这个键/值是相应torrent文件的统计数据。每一个键(key)由20字节的二进制info_hash组成。而该键所对应的值也是dictionary,它包含如下的键(key):

    complete:完成文件下载的peer数,即做种者的数量。(整数类型)

    downloaded:已向tracker注册的下载完成的总次数("event=complete",即一个客户端完成了下载) 。(整数类型)

    incomplete:非做种的peers数(还没有完成该文件下载的peers数),即“占他人便宜者”。 (整数类型)

    name:(可选的),torrent的内部名,由.torrent文件中info键所对应值中的name指定。

    注意这个响应有三层dictionary嵌套。例如:

    d5:filesd20:....................d8:completei5e10:downloadedi50e10:incompletei10eeee

    表示....................是一个20字节的info_hash,有5个做种者,5个正在下载者以及50个已经完成的下载。

    Unofficial extensions to scrape(scrape的非正式扩展)

    下面的响应键是非官方的。因为它们都是非官方的,因此都是可选的。

     

    • failure reason:可读的错误信息,这个信息告诉客户端请求失败的原因(字符串类型)。使用该键的知名客户端:Azureus。
    • flags:这是一个B编码的dictionary,它包含多个标志。flags键对应的值是另一个嵌套的dictionary,可能包含如下键(key):
    • min_request_interval:这个键所对应的值是一个整数,该整数指定两个scraping操作之间的最小间隔秒数。发送该键(key)的知名Tracker是BNBT。使用该键(key)的知名客户端是Azureus。

    Peer wire protocol (TCP)



    概述

    peer(端)协议使片(piece)的交换变得容易,片的描述请参考元信息文件。

    注意:原来的规范在描述peer协议时,也使用术语piece“(片)”,但是这不同于元信息文件里面的术语“piece(片)”,由于这个原因,在本规范中,将使用术语“块(block)”来描述peers(端)之间交换的数据。

    一个客户端(client)必须维持其与每一个远程peer(端)连接的状态信息:

    • choked: 远程peer(端)是否已经choke本客户端。当一个peer(端) choke本客户端后,它是在通知本客户端,除非它unchoke本客户端,否则它不会应答该客户端所发出的任何请求。本客户端也不应该试图向远程peer发送数据请求,并且应该认为所有没有应答的请求已经被远程peer丢弃。
    • interested: 远程peer(端)是否对本客户端提供的数据感兴趣。这是远程peer在通知本客户端,当本客户端unchoke他们时,远程客户端将开始请求块(block)。

    注意这也意味着本客户端需要记录它是否对远程peer(端)感兴趣,以及它是否choke/unchoke远程peer。因此真正的列表看起来像这样:

    • am_choking: 本客户端正在choke远程peer。
    • am_interested: 本客户端对远程peer感兴趣。
    • peer_choking: 远程peer正choke本客户端。
    • peer_interested: 远程peer对本客户端感兴趣。

    客户端连接开始时状态是choke和not interested(不感兴趣)。换句话就是:

    • am_choking = 1
    • am_interested = 0
    • peer_choking = 1
    • peer_interested = 0

    当一个客户端对一个远程peer感兴趣并且那个远程peer没有choke这个客户端,那么这个客户端就可以从远程peer下载块(block)。当一个客户端没有choke一个peer,并且那个peer对这个客户端这个感兴趣时,这个客户端就会上传块(block)。


    客户端必须不断通知它的peers,它是否对它们感兴趣,这一点是很重要的。客户端和每个端的状态信息必须保持最新,即使本客户端被choke。这允许所有的peer知道,当它们unchoke该客户端后,该客户端是否开始下载(反之亦然)。

    数据类型

    如果没有用其他的方法指定,在peer wire协议中的所有整数都会编码为4个字节的大端(big-endian)值。这也包括在握手之后,所有报文(Message)的长度前缀。

    报文流(Message flow)

    (译者注:因为ICMP-Internet控制报文协议中的Message翻译成报文,同时IP/TCP层中传输的数据都翻译为数据报,应用层传输的数据都翻译成报文,因此在这里Message翻译成报文)

    peer wire协议由一个初始的握手组成。握手之后,peers通过以长度为前缀消息的交换进行通信。长度前缀就是上面描述的整数。

    握手(HandShake)

    握手是一个必需的报文,并且必须是客户端发送的第一个报文。该握手报文的长度是(49+len(pstr))字节。

    握手:handshake:

    • pstrlen: 的字符串长度,单个字节。
    • pstr: 协议的标识符,字符串类型。
    • reserved: 8个保留字节。当前的所有实现都使用全0.这些字节里面的每一个字节都可以用来改变协议的行为。来自Bram的邮件建议应该首先使用后面的位,以便可以使用前面的位来改变后面位的意义。
    • info_hash: 元信息文件中info键(key)对应值的20字节SHA1哈希。这个info_hash和在tracker请求中info_hash是同一个。
    • peer_id: 用于唯一标识客户端的20字节字符串。这个peer_id通常跟在tracker请求中传送的peer_id相同(但也不尽然,例如在Azureus,就有一个匿名选项)。

    在BitTorrent协议1.0版本,pstrlen = 19, pstr = “BitTorrent protocol”。


    连接的发起者应该立即发送握手报文。如果接收方能够同时地服务多个torrent,它会等待发起者的握手报文(torrent由infohash唯一标识)。尽管如此,一旦接收方看到握手报文中的info_hash部分,接收方必须尽快响应。tracker的NAT-checking特性不会发送握手报文的peer_id字段。


    如果一个客户端接收到一个握手报文,并且该客户端没有服务这个报文的info_hash,那么该客户端必须丢弃该连接。


    如果一个连接发起者接收到一个握手报文,并且该报文中peer_id与期望的peer_id不匹配,那么连接发起者应该丢弃该连接。注意发起者可能接收来自tracker的peer信息,该信息包含peer注册的peer_id。来自于tracker的peer_id需要匹配握手报文中的peer_id。

    peer_id

    peer_id长20个字节。至于怎么将客户端和客户端版本信息编码成peer_id,现在主要有两种惯例:Azureus风格和Shadow风格。


    Azureus风格使用如下编码方式:’-’, 紧接着是2个字符的client id,再接着是4个数字的版本号,’-’,后面跟着随机数。


    例如:'-AZ2060-'...

    使用这种编码风格的知名客户端是:

    ......

    另外还需要识别的客户端有:

    • 'BD' (例如: -BD0300-)
    • 'NP' (例如: -NP0201-)
    • 'SD' (例如: -SD0100-)
    • 'wF' (例如: -wF2200-)
    • 'hk' (例如: -hk0010-) 中国IP地址,IP address, unrequestedly sends info dict in message 0xA, reconnects immediately after being disconnected, reserved bytes = 01,01,01,01,00,00,02,01

    Shadow风格使用如下编码方式:一个用于客户端标识的ASCII字母数字,多达五个字符的版本号(如果少于5个,则以’-’填充),紧接着是3个字符(通常是’---’,但也不总是这样),最后跟着随机数。版本字符串中的每一个字符表示一个0到63的数字。'0'=0, ..., '9'=9, 'A'=10, ..., 'Z'=35, 'a'=36, ..., 'z'=61, '.'=62, '-'=63。


    你可以在找到关于shadow编码风格(包含关于版本字符串后的三个字符用法的习惯)的详细说明。


    例如:用于Shadow 5.8.11的’S58B-----‘...


    使用这种编码风格的知名客户端是:

    Bram的客户端现在使用这种风格:'M3-4-2--' or 'M4-20-8-'。


    BitComet使用不同的编码风格。它的peer_id由4个ASCII字符’exbc’组成,接着是2个字节的x和y,最后是随机字符。版本号中的x在小数点前面,y是版本号后的两个数字。BitLord使用相同的方案,但是在版本号后面添加’LORD’。BitComet的一个非正式补丁曾经使用’FUTB’代替’exbc’。自版本0.59开始,BitComet peer id的编码使用Azureus风格。


    XBT Client也使用其特有的风格。它的peer_id由三个大写字母’XBT’以及紧随其后的代表版本号的三个ASCII数字组成。如果客户端是debug版本,第七个字节是小写字符’d’,否则就是’-‘。接着就是’-‘,然后是随机数,大写和小写字母。例如:peer_id的开始部分为'XBT054d-'表明该客户端是版本号为0.5.4的debug版本。


    Opera 8 previews and Opera 9.x releases使用以下的peer_id方案:开始的两个字符是’OP’,后面的四个数字是开发代号。接着的字符是随机的小写十六进制数字。


    MLdonkey使用如下的peer_id方案:开始的字符是’-ML’,后面跟着点式版本,然后就是一个’-’,最后跟着随机字符串。例如:'-ML2.7.2-kgjjfkd'。


    Bits on Wheels使用模式'-BOWxxx-yyyyyyyyyyyy',其中y是随机的(大写字母),x依赖于版本。如果版本为1.0.6,那么xxx = AOC。


    Queen Bee使用Bram的新风格:'Q1-0-0--' or 'Q1-10-0-'之后紧随着随机字节。


    BitTyrant是Azureus的一个分支,在它的1.1版本,其peer id使用'AZ2500BT' + 随机字节的方式。


    TorrenTopia版本1.90自称是或源自于Mainline 3.4.6。它的peer ID以'346------'开始。


    BitSpirit有几种编码peer ID的方式。一种模式是读取它的peer ID然后使用开始的八个字节作为它peer ID的基础来重新连接。它的实际ID使用'\0\3BS'(c 标记法)作为版本3.x的前四个字节,使用'\0\2BS'作为版本2.x的前四个字节。所有方式都使用'UDP0'作为结尾。


    Rufus使用它的十进制ASCII版本值作为开始的两个字节。第三个和第四个字节是'RS'。紧随其后的是用户的昵称和一些随机字节。


    G3 Torrent的peer ID以’-G3’开始,然后追加多达9个表示用户昵称的字符。


    FlashGet使用Azureus风格,但是前面字符是’FG’,没有’-’。版本 1.82.1002 仍然使用版本数字 '0180'。


    BT Next Evolution源自于BitTornado,但是试着模仿Azureus风格。结果是它的peer ID以’-NE’开始,接着是四个数字的版本号,最后就是以shadow peer id风格描述客户端类型的三个字符。


    AllPeers takes the sha1 hash of a user dependent string(这个不好翻译,待译),使用"AP" + version string + "-"代替开始的一些字符。


    Qvod的id以四个字母"QVOD"开始,接着是4个十进制数字的开发代号(目前是” 0054”)。最后的12个字符是随机的大写十六进制数字。中国有一个修改版,该版本以随机字节代替前四个字符。

    许多客户端全部使用随机数或者随机数后面跟12个全0(像Bram客户端的老版本)。

    报文(Messages)

    接下来协议的所有报文采用如下的结构:。length prefix(长度前缀)是一个4字节的大端(big-endian)值。message ID是单个十进制值。playload与消息相关。

    • keep-alive:

    keep-alive消息是一个0字节的消息,将length prefix设置成0。没有message ID和payload。如果peers在一个固定时间段内没有收到任何报文(keep-alive或其他任何报文),那么peers应该关掉这个连接,因此如果在一个给定的时间内没有发出任何命令的话,peers必须发送一个keep-alive报文保持这个连接激活。通常情况下,这个时间是2分钟。

    • choke:

    choke报文长度固定,并且没有payload。

    • unchoke:

    unchoke报文长度固定,并且没有payload。

    • interested:

    interested报文长度固定,并且没有payload。

    • not interested:

    not interested报文长度固定,并且没有payload。

    • have:

    have报文长度固定。payload是piece(片)的从零开始的索引,该片已经成功下载并且通过hash校验。


    实现者注意:实际上,一些客户端必须严格实现该定义。因为peers不太可能下载他们已经拥有的piece(片),一个peer不应该通知另一个peer它拥有一个piece(片),如果另一个peer拥有这个piece(片)。最低限度”HAVE suppresion”会使用have报文数量减半,总的来说,大致减少25-35%的HAVE报文。同时,给一个拥有piece(片)的peer发送HAVE报文是值得的,因为这有助于决定哪个piece是稀缺的。


    一个恶意的peer可能向其他的peer广播它们不可能下载的piece(片)。Due to this attempting to model peers using this information is a bad idea. 


    • bitfield:

    bitfield报文可能仅在握手序列发送之后,其他消息发送之前立即发送。它是可选的,如果一个客户端没有piece(片),就不需要发送该报文。


    bitfield报文长度可变,其中x是bitfield的长度。payload是一个bitfield,该bitfield表示已经成功下载的piece(片)。第一个字节的高位相当于piece索引0。设置为0的位表示一个没有的piece,设置为1的位表示有效的和可用的piece。末尾的冗余位设置为0。


    长度不对的bitfield将被认为是一个错误。如果客户端接收到长度不对的bitfield或者bitfield有任一冗余位集,它应该丢弃这个连接。

    • request:

    request报文长度固定,用于请求一个块(block)。payload包含如下信息:

      • index: 整数,指定从零开始的piece索引。
      • begin: 整数,指定piece中从零开始的字节偏移。
      • length: 整数,指定请求的长度。
    This section is under dispute! Please use the discussion page to resolve this!

    根据官方规范有关主要版本3,“所有当前执行应使用 2^15(32 KB),请求数量大于 2^17(128 KB)时应断开连接。”在主要版本4中,此反应修改到了 2^14(16 KB),超过该值的用户会强迫拒绝。注意到块请求小于片断大小(>=2^18 字节),所以为下载一个完整片断需要多次请求。

    由于新版本将限制定在 16 KB,尝试使用 32 KB 的块就好比用 4 发子弹来玩俄式轮盘——会遇到困难。更小的请求会导致更大的系统时间和空间开销,因为要跟踪很多请求。结果应使用所有客户端都允许的 16 KB。

    请求块大小的限制执行的选择没有减少一部分清楚。在主要版本 4 中,强制使用 16 KB 的请求,许多客户端会使用该值,只有一个严格客户端组不会使用。大多数旧客户端使用 32 KB 请求,不允许明显减少可能用户的批次。同时 16 KB 是现在部分官方的限制(“部分”是因为官方协议文档没有更新),所以强制使用没有错。另外,允许更大的请求增大了可能用户的批次,除在非常低的带宽连接(小于 256 kbps)中,多个块会在一个阻塞周期内完成下载,从而强迫使用旧的限制仅会降低很少的性能。因此,推荐仅在旧的 128 KB 下才强行限制。

    • piece:

    piece报文长度可变,其中x是块的长度。payload包含如下信息:

      • index: 整数,指定从零开始的piece索引。
      • begin: 整数,指定piece中从零开始的字节偏移。
      • block: 数据块,它是由索引指定的piece的子集。
    • cancel:<id<=8 style="word-wrap: break-word;">

    cancel报文长度固定,用于取消块请求。playload与request报文的playload相同。一般情况下用于结束下载。

    • port:

    port报文由新版本的Mainline发送,新版本Mainline实现了一个DHT tracker。该监听端口是peer的DHT节点正在监听的端口。这个peer应该插入本地路由表(如果支持DHT tracker的话)。

    Algorithms


    排队

    通常建议用户在每个连接上保持一些未完成的请求。因为从一块的下载到开始下载另一块需要一个完全的往返程(往返程在片断消息和下一个请求消息之间)。一旦与高 BDP (Bandwidth Delay Product,高延迟或带宽)相连,会降低很多性能。官方客户端未完成请求的默认值是 5。

    用户注意:这是最严格的性能条款。一个 5 请求的静态队列对于具有 50 ms 延迟 5 Mbps 中的 32 KB 块是合理的。连接更大的带宽越来越常见,所以用户界面设计者被催促使其对于改变更适用。特别地,线缆调制解调器以调整通信量和增加其可能缓和部分由此导致的问题,这是众所周知的。

    自动调整:调整此参数的一个合理的方法是连续测量单个连接的带宽。如果该用户增加带宽而队列不够,则尝试增加队列长度。使用相同标志如果减少队列长度没有减少带宽和延迟,可能是队列长度太大了。

    超级种子

    (该项不是原始规范的一部分)

    在 S-5.5 中的超级种子特性是一种新的做种算法,用来帮助只有有限带宽的做种者发布很大的文件,减少为了产生新的种子而上传的数据总量。

    当做种者使用“超级种子模式”时,它不会作为标准种子,而是伪装成一个没有数据的普通客户端。当客户端连接时,它会通知它们自己收到一块从未发送或源很少的片断。这将促使客户端仅尝试下载那个片断。

    当客户端完成下载该片断时,做种者看到它以前发送的片断在其他用户中至少有一个拥有后,才会继续发送另外的片断。在那之前,客户端下载不到做种者的其他片断,这样不会浪费做种者的带宽。

    这种方法会有更高的效率,同时促使用户只下载源最少的数据,降低了多余数据的发送,限制了没有为该群传输数据的用户而发送的数据量。在这之前,做种者可能需要上传文件总大小的 1.5 到 2 倍,其他用户才可能成为种子。但是,使用超级种子模式的单个客户端发布大的文件只需上传文件大小的 1.05 倍就能成为种子。这是标准做种效率的 1.5 到 2 倍。

    不推荐一般用户采用超级种子模式。虽然它有助于稀少数据的扩散,但是它限制了客户端下载片断的选择,同时限制了客户端下载自己已得到部分的片断。所以,超级种子模式只推荐原始做种者使用。

    综上,“原始做种者模式”或“发布者模式”是更恰当的名称。

    片断下载策略

    客户端可以随机顺序下载片断。

    更好的方法是首先下载源最少的片断。客户端可以从每个其他用户保存的原始位字段来决定,通过拥有消息来更新。然后,客户端可以下载出现在其他用户位字段中频率最低的片断。

    最后阶段

    下载接近完成时,最后几块的速度有变慢的趋势。为了加速,客户端向其他所有拥有自己缺少块的用户发送请求。为防止变成无效,客户端在每个块完成后就向其他用户发送一个取消的消息。

    没有已定的界限,推荐百分比或能用来作为指导的块计数。

    何时进入最后阶段模式有待讨论。一些客户端以请求了所有块来进入最后阶段。其他的等到剩余块的数目少于传输中块的数目或不超过 20 来进入该阶段。保持很少的等待块(1 或 2 块)来将允许值减到最少,如果随机选择块的请求,可能会重复下载到已有的块。更多的协议说明见:http://hal.inria.fr/inria-00000156/en

    阻塞与最佳畅通

    阻塞有几种原因。TCP 拥塞控制在同时发出许多连接时表现很差。同时,阻塞使每个用户使用 tit-for-tat-ish 算法来确定自己得到一致的下载速度。

    下面描述的阻塞算法是现在使用的。所有新算法同时在整个包括它们的网络中工作正常,这是很重要的。

    一个好的阻塞算法应有几个标准。它应为更高的 TCP 性能改进并发上传数。它应避免被叫做“原纤化作用”的快速阻塞和未阻塞。它应互换到让自己下载的用户。最后,它应偶尔测试未使用连接来发现是否比当前使用的更好,这叫做最佳畅通。

    当前使用的阻塞算法通过每 10 秒钟改变被阻塞用户来避免原纤化作用。

    互换和上传数目的改进是由拥有最佳上传速度和感兴趣的 4 个未阻塞用户来控制的。这将使客户端的下载速度变得最大。这 4 个用户被称为下载者,因为它们对从客户端的下载感兴趣。

    与下载者相比,具有较高上传速度的用户对未阻塞不感兴趣。如果它们感兴趣,具有最低上传速度的下载者将被阻塞。如果客户端拥有完成的文件,它使用上传速度而不是下载速度来决定哪一个用户未阻塞。

    对于最佳畅通,在任何时候只有一个未阻塞用户,而不管它的上传速度(如果感兴趣,它会成为 4 个允许的下载者之一)。最佳畅通的用户 30 秒循环一次。最新连接的用户有 3 次可能作为循环中当前的最佳畅通。这给它们得到一个完成块就上传的机会。

    反冷落

    (扩展不在官方协议中)

    偶尔一个 BitTorrent 用户会被其他用户冷落,它先前从那些用户中下载了数据。在这种情况下,它通常得到很低的下载速度,直到最佳畅通找到更好的用户。为了缓和此问题,当过了 1 分钟而没有从某个用户得到一个片断,BitTorrent 认为它被那个用户“冷落”了,不上传给那个用户(除了最佳畅通以外)。这会频繁导致超过 1 个的用户同时变成最佳畅通(一个例外是最佳畅通,规则如上所述),它会使波动的下载速度回复得更快。


    Official Extensions To The Protocol



    Reserved Bytes



    举报

    相关文章推荐

    什么是BitTorrent协议

    一般的下载服务器为每一个发出下载请求的用户提供下载服务,而BitTorrent的工作方式与之不同。分配器或文件的持有者将文件发送给其中一名用户,再由这名用户转发给其它用户,用户之间相互转发自己所拥有的...

    [转载] BitTorrent协议规范

    BitTorrent 协议规范(BT协议集合)一 BitTorrent 是一种分发文件的协议。它通过URL来识别内容,并且可以无缝的和web进行交互。它基于HTTP协议,它的优势是:如果有多个下载者并...

    精选:深入理解 Docker 内部原理及网络配置

    网络绝对是任何系统的核心,对于容器而言也是如此。Docker 作为目前最火的轻量级容器技术,有很多令人称道的功能,如 Docker 的镜像管理。然而,Docker的网络一直以来都比较薄弱,所以我们有必要深入了解Docker的网络知识,以满足更高的网络需求。

    BitTorrent协议

    BitTorrent协议  作者:小王子    撰写日期:2011.09              最近在看一个用peersim模拟的BitTorrent文件共享系统,由于之前...

    BitTorrent DHT 协议中文翻译

    前言 做了一个磁力链接和BT种子的搜索引擎 {Magnet & Torrent},因此把 DHT 协议重新看了一遍。 原文:DHT Protocol 译文:BitTorrent DHT 协议中文...

    BiTtorrent的DHT算法 --- Kademlia 协议原理简介 分享

    BiTtorrent的DHT算法 --- Kademlia 协议原理简介 分享Kademlia 协议原理简介 ()一、前言  Kademlia协议(以下简称Kad)是美国纽约大学的PetarP. Ma...
    • zfrong
    • zfrong
    • 2011-06-29 00:20
    • 1226

    BitTorrent协议java实现分析

    BitTorrent学习最近做的项目里,利用开源的NanoHttpd搭建了嵌入安卓的服务器。最近要做的一部分工作,就是基于BitTorrent协议的WIFI下载实现。协议介绍普通的HTTP/FTP下载...

    【P2P网络】BitTorrent的DHT协议(译自官方版本)

    译者前序 DHT协议早在2005年就已经成为了官方BitTorrent协议的一部份,但是我竟然一直没有找到国内的官方翻译稿,所以将其进行翻译,若文中错误,欢迎各位指正。 其次,若想彻底理解DHT协...

    【转】BitTorrent协议详解

    原文地址:http://bbs.nju.edu.cn/bbstcon?board=BitTorrent&file=M.1209531185.A 感谢原作者 BitTrrent(简称BT,比特洪流)...
    返回顶部
    收藏助手
    不良信息举报
    您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
    举报原因:
    原因补充:

    (最多只允许输入30个字)