资讯详情

[转载] BitTorrent协议规范

BitTorrent 协议规范(BT协议集合)一 BitTorrent 是分发文件的协议。它通过URL识别内容,可以无缝和谐web进行交互。它基于HTTP协议的优点是,如果多个下载者并发下载相同的文件,每个下载者也将文件上传给其他下载者,以便文件源可以支持大量用户下载,只带来适当的负载增长。(注:由于大量负载均衡到整个系统,提供源文件的机器负载只有少量增加) 一个BT文件分布系统由以下实体组成: 一个普通的web服务器 静态元信息文件 一个跟踪(tracker)服务器 终端用户的web浏览器 终端下载者 多个终端用户下载同一文件的理想情况。 为了提供文件共享,主机需要执行以下步骤: ?运行一个 tracker服务器(或者已经有了tracker服务器运行时也可以) ?运行一个web例如,服务器apache,或者已经有一个了web服务器在运行。 ?在web在服务器上扩展文件的名称.torrent 和MIME类型 application/x-bittorrent关联(或已关联) ?根据 tracker服务器的 URL 创建元信息文件(.torrent)。 ?发布元信息文件web服务器上 ?在某个web在页面上添加一个链接到元信息文件。 ?运行拥有完整文件的下载者(成为origin’,或者’seed’,种子) 为了开始下载文件,终端用户执行以下步骤: ?安装 BT(或已安装) ?访问提供 .torrent 文件的web服务器 ?点击到 .torrent 文件链接(译注:此时,bt会弹出对话框) ?选择在哪里保存下载的文件?或者断点续传 ?等待下载完成。 ?结束bt程序运行(如果不主动结束,那么bt将永远为他人提供文件上传) 各部分之间的连通性如下: 该网站负责提供静态文件BT在客户端机器上放置辅助程序(客户端)。 Trackers从所有下载者处接收信息,并随机返回peers列表。这种交互是通过的HTTP或HTTPS协议完成。 下载者向下载者tracker登记,使得tracker它们的进度可以理解;数据通过下载者之间的直接连接上传和下载。这种连接使用 BitTorrent 基于对等协议TCP。 Origin只负责上传,从不下载,因为它有完整的文件。Origin是必须的。 元文件和tracker响应采用简单、有效、可扩展的格式,称为bencoding,它可以包含字符串和整数。由于不必要的字典关键字可以忽略,这种格式是可扩展的,其他选项可以很容易地添加到未来。 Bencoding格式如下: 对于字符串,首先是字符串的长度,然后是冒号,然后跟随实际字符串,如:4:spam,就是“ spam” 以下是整数编码 ‘i’ 开始,然后是10进制的整数值,最后是e结束。i3e表示3,I-3e表示-3。整数没有大小限制。I-0e无效。除此之外 i0e所有从0开始的整数都无效。I0e当然表示0。 列表编码如下,以l’开始,接下来是列表值的编码(也采用bencoded编码),最后用‘’,e例如:l4:spam4:eggse 表示 [‘spam’, ‘eggs’]。 字典编码如下,以d开始,下一步是可选的keys与之对应的值最户以e’结束。例如:d3:cow3:moo4:spam4: eggse,表示{‘cow’:’moo’,’spam’:’eggs’},而d4:spaml1:al:bee 表示 {‘spam’:[‘a’,’b’]}。键值必须是字符串,并且已经排序(不是按字母顺序排序,而是按原始字符串排序)。 采用元文件bencoded编码字典包括以下关键字: announce tracker的服务器 info 它实际上是一个字典,包括以下关键字: Name: 在保存文件时,将字符串作为建议值。这只是一个建议,你可以用其他名字保存文件。 Piece length: 为了更好地传输,文件被分成等长的片段,除了最后一个片段,这个值就是片段的大小。片段大小几乎总是2米,最常用的是 256k(BT前一个版本3.2,用的是1M作为默认大小) Pieces: 一个长度为20的整数倍的字符串。它将被分成20字节长的字符串,每个字符串都是相应的片段hash值。 另外,还有一个length或files这两个关键字只能出现一个关键字。如果是的话length,如果是的话,只有单个文件需要下载files所以要下载的是目录中的多个文件。 如果是单个文件,那么length是文件的长度。 为了支持其他关键字,对于多个文件,它也被视为一个文件,即根据文件的顺序连接每个文件的信息,形成一个字符串。每个文件的信息实际上都是一个字典,包括以下关键字: Length:文件长度 Path:子目录名称的列表,列表的最后一个是文件的实际名称。(不允许列表为空)。 Name:在单文件的情况下,name在多文件的情况下,是文件的名称,name是目录的名称。 Tracker查询。Trakcer通过HTTP的GET命令的参数接收信息,方(即下载者)的响应是通过bencoded编码信息。请注意,尽管目前tracker实现需要一个web事实上,服务器可以更轻地运行,例如,apache模块。 Tracker GET requests have the following keys: 发送给Tracker的GET请求包括以下关键字: Info_hash: 元文件中info部分的sha hash,20字节长。这个字符创作几乎必须被转义(翻译注:在URL有些字符不能出现,必须通过unicode进行编码) Peer_id: 下载者的id,一个20字节长的字符串。在开始新下载之前,每个下载者都需要随机创建这个id。字符串通常需要转义。 Ip: 给出可选参数peer的ip地址(或者dns名字?)。通常用于origin假如它和tracker在同一台机器上。 Port: peer监控端口。下载者通常在 6881 如果端口被占用,它将永远尝试 如果6889都被占用,那就放弃监控。 Uploaded: 十进制表示已上载的数据大小。 Downloaded: 十进制表示已下载的数据大小 Left: 该peer还有多少数据没有下载完,十进制表示。注意,这个值不能根据文件长度和已下载数据大小计算出来,因为很可能是断点续传,如果因为检查文件完整性失败而必须重新下载的时候,这也提供了一个机会。 Event: 可选关键字值started、compted或者stopped其中一个(也可以是空的,不处理)。如果没有关键字,。在下载开始时,该值被设置为started,下载完成后,设置为completed。如果下载者停止下载,则该值设置为stopped。 Tracker的响应是用bencoded编码字典。如果tracker响应中有一个关键字failure reason,然后它对应一个字符串来解释查询失败的原因,其他关键字不再需要。否则,它必须有两个关键字:Interval:下载者在两次发送请求之间的时间间隔。Peers:每个字典列表包括以下关键字:Peer id,Ip,Port,分别对应peer所选择的id、ip地址或者dns名称,端口号。请注意,如果发生某些事件,或者需要更多peers,然后下载者可能会不时发送请求, (downloader 通过 HTTP 的GET 命令来向 tracker 发送查询请求,tracker 响应一个peers 的列表) 如果你想对元信息文件或tracker扩展查询,需要相同Bram Cohen协调确保所有扩展都是兼容的。 BT基于对等协议TCP,它非常高效,不需要设置任何设置socket选项:BT对等协议是指peer与peer交换信息的协议) 对等的两个连接是对称的,信息在两个方向上传输相同,数据可以在任何方向流动。 一旦某个peer下载一个片段片段并检查了它的完整性后,它就会把它交给它peers宣布它有这个片段。 连接的任何一端都包含两个比特状态信息:是否choked,是否感兴趣。Choking通知对方,除非没有数据可以发送,否则没有数据可以发送unchoking发生。Choking技术后解释的原因和原因。 一旦一端状态变成interested,另一端变成非choking,然后数据传输就开始了。(也就是说,一个peer,如果你想从它的某个 peer如果在那里获得数据,它必须首先将两者之间的连接设置为 interested,其实就是过去发消息,而另一个peer,要检查它是否应该给这个家伙发送数据,如果它对这个家伙是 unchoke,然后可以给它发数据,否则还是不能给它数据)Interested必须始终设置状态――任何时候。使用一些技能来更好地实现这一目标,但它允许下载者立即知道什么peers将开始下载。使用一些技能来更好地实现这一目标,但它允许下载者立即知道什么peers将开始下载。 平等协议从握手开始,其次是循环消息流。每个消息的前面都有一个数字来表示消息的长度。握手的过程是先发19,再发BitTorrent protocol”。19就是“BitTorrent protocol”的长度。 所有后续整数均采用big-endian 编码为4个字节 协议名称后,有8个保留字节,目前设置为0。 接下来在元文件中 info 信息,通过 sha1 计算后得到的 hash值,20个字节长。接收消息方,也会对 info 进行一个 hash 计算,如果这两个结果不同,说明对方想要的文件不是自己提供的,所以切断连接。 接下来是20个字节 peer id。 这就是握手的过程 接下来是从消息长度开始的消息流,这是可选的。长度为0 用于保持连接的活动状态的消息被忽略了。这样的消息通常每2分钟发送一次。 其他类型的新闻都有字节长的新闻类型,可能值如下: ‘choke’, ‘unchoe’, ‘interested’, not interested该类型的新闻不再包含其他数据。 ‘bitfield’永远也仅是第一个被发送的消息。它的数据实际是一个位图,如果downloader已经发送了某个片断,那么对应的位置1,否则置0。Downloaders如果一个片断也没有,可以忽略这个消息。(通过这个消息,能知道什么了?) ‘have’类型的消息,后面的数据是一个简单的数字,它是下载者刚刚下载完并检查过完整性的片断的索引。(由此,可以看到,peer通过这种消息,很快就相互了解了谁都有什么片断) ‘request’类型的消息,后面包含索引、开始位置和长度)长度是2的幂。当前的实现都用的是215 ,而关闭连接的时候,请求一个超过2 17的长度。(这种类型的消息,就是当一个peer希望另一个peer给它提供片断的时候,发出的请求) ‘cancel’类型的消息,它的数据和’request’消息一样。它们通常只在下载趋向完成的时候发送,也就是在‘结束模式“阶段发送。在一次下载接近完成的时候,最后的几个片断需要很长时间才能下载完。为了确保最后几个片断尽快下载完,它向所有的peers发送下载请求。为了保证这不带来可怕的低效,一旦某个片断下载完成,它就其它peers发送’cancel’消息。(意思就是说,我不要这个片断了,你要是准备好了,也不用给我发了,可以想象,如果对方还是把数据发送过来了,那么这边必须忽略这些重复的数据)。 ‘piece’类型的消息,后面保护索引号、开始位置和实际的数据。注意,这种类型的消息和 ‘request’消息之间有潜在的联系(译注:因为通常有了request消息之后,才会响应‘piece’消息)。如果choke和unchoke消息发送的过于迅速,或者,传输速度变的很慢,那么可能会读到一些并不是所期望的片断。( 也就是说,有时候读到了一些片断,但这些片断并不是所想要的) BitTorrent 协议规范(BT协议集合)二 这个翻译版本由孤波独立完成 原文见http://bitconjurer.org/BitTorrent/protocol.html 作者Bram Cohen 孤波享有对该翻译版本解释权修改权 非商业引用请注明译者 BitTorrent协议详解 BitTrrent(简称BT,比特洪流)是一个文件分发协议,它通过URL识别内容并且和网络无缝结合。它在HTTP平台上的优势在于,同时下在一个文件的下载者在下载的同时不断互相上传数据,使文件源可以在很有限的负载增加的情况下支持大量下载者同时下载。 一个BT式文件分发需要以下实体: ·一个普通网络服务器 ·一个静态元信息文件 ·一个BT Tracker ·一个“原始”下载者 ·网络终端浏览者 ·网络终端下载者 这里假设理想情况下一个文件有多个下载者。 架设一个BT服务器步骤如下: 1.开始运行Tracker(已运行的跳过这一步); 2.开始运行普通网络服务器端程序,如Apache,已运行的跳过这一步; 3.在网络服务器上将.torrent文件关联到Mimetype类型application/x-bittorrent(已关联的跳过这一步); 4.用要发布的完整文件和Tracker的URL创建一个元信息文件(.torrent文件); 5.将元信息文件放置在网络服务器上; 6.在网页上发布元信息文件(.torrent文件)链接; 7.原始下载者提供完整的文件(原本)。 通过BT下载步骤如下: 1.安装BT客户端程序(已安装的跳过这一步); 2.上网; 3.点击一个链到.torrent文件的链接; 4.选择本地存储路径,选定需要下载的文件(对有选择下载功能的BT客户端用户); 5.等待下载完成; 6.用户退出下载(之前下载者不停止上传)。 连接状况如下: ·网站正常提供静态文件连接,并且启动客户端上的BT程序; ·Tracker即时接收所有下载者信息,并且给每个下载者一份随机的peer列表。通过HTTP或HTTPS协议实现; ·下载者每隔一段时间连一次Tracher,告知自己的进度,并和那些已经直接连接上的peer进行数据的上传下载。这些连接遵循BitTorrent peer协议,通过TCP协议进行通信。 ·原始下载者只上传不下载,他拥有整个文件,所以很必要向网络中传输完文件的所有部分。在一些人气很旺的下载中,原始下载者经常可以在较短的时间内退出上传,由其它已经下载到整个文件的下载者继续提供上传。 元信息文件和Tracker的回应信息都以一种简单高效可扩展的格式(Bencoding,B编码)传送。B编码过的信息就是以包含字符串和整型数据的字典和列表的嵌套(像在Python中一样),可扩展性是指可以通过减少字典忽略的关键值来添加新的特性。 B编码规则如下: ·字符串表示为十进制数的既定字符串长度加冒号再跟原字符串。 如4:spam就相当于'spam'。 ·整型数据表示成前面加'i'后面加'e'中间是十进制数,如i3e就相当于3,i-3e就是-3。整型数据没有长度限制。i-0e无效,所有以'i0'开头的除了代表0的i0e,其它都无效。 ·列表编码为一个'l'开头后面跟它所包含的项目(已经编码过)最后加一个'e',比如l4:spam4:eggse就等于['spam', 'eggs']。 ·字典编码为一个'd'开头后面跟一个交替关键值(key)及其对应值的列表最后加一个'e'。 如:d3:cow3:moo4:spam4:eggse相当于{'cow': 'moo', 'spam': 'eggs'} d4:spaml1:a1:bee相当于{'spam': ['a', 'b']} 关键值必须是处理过的字符串(用原始字符串编码的,而且不是数字字母混合编码的)。 元信息文件就是B编码的有以下关键值的字典: announce(声明) Tracker的URL。 info(信息) 此关键值对应一个字典包含以下描述的关键值: 关键值name对应一个字符串,代表默认的下载文件或存成目录的名字。它是纯粹建议性的。 关键值piece length(块长)对应文件分割成的块的字节数。出于传输需要,文件被分割成大小相等的块,除了最后一块通常会小一些。块长一般来说是2的权值,大部分设块长为256K(2的18次幂)。 关键值pieces(块)对应一个字符串,此字符串长度是20的倍数。它可以再分成每20字节一段的多个字符串,分别对应块在索引中的SHA1校验码(hash)。 还有关键值length(长度)和files(文件),它们不能同时出现也不能都不出现。当length出现说明这个元信息文件只是单文件下载,否则说明是多文件的目录结构下载。 单文件情况下,length对应文件长度的字节数。 多文件情况被看作是把许多单文件按文件列表中的顺序连成一个大文件下载,而关键值files就对应文件列表,是一个字典的列表,其中每个字典又包含以下关键值: length(长度) 文件长度的字节数。 path(路径) 一个包含字符串的列表,字符串就是子目录名,最后一项的字符串是文件名。 (一个长度为零的length表单是错误的。) 在单文件情况下,关键值name是文件名;多文件情况下,它就成了目录名。 Tracker质询是双向的。Tracker通过HTTP GET参数获得信息,然后返回一个B编码后的信息。尽管Tracker需要在服务器端执行,但它运行流畅像Apache的一个模块。 Tracker的GET请求有如下关键值: info_hash 20字节长的SHA1验证码,来自B编码过的元信息文件中的info值下,是元信息文件的一个支链。这个值是自动转换的。 peer_id 一个20字节长的字符串,是每个用户开始下载时随机生成的ID。这个值也是是自动转换的。 ip 一个可选择的参数给出peer所在的IP(或DNS主机名),一般是和Tracker同机器的原始下载者得到后以便散发文件。 port 监听端口,官方默认的是从6881端口开始试,如果端口被占用则依次向后推一个端口找空闲端口,到6889端口为止。 uploaded 目前总上传量,编码为十进制ASCII码。 downloaded 目前总下载量,编码为十进制ASCII码。 left 未下载的字节数,编码为十进制ASCII码。这个数不是通过文件长度和已下载数算出来的,因为文件可能在被续传,还有一些已经下载的数据不能通过完整性检查必须重新下载。 event 这是个选择性的关键值,选项有started,completed或stopped(或empty,等同于没有运行)。如果没有运行,这个声明会定期间隔一定时间发出。开始下载时发出started值,完成下载时发出completed。当文件完整后再开始,没有completed发出,下载者中止下载时发出stopped。 Tracker的回应也是B编码字典。如果Tracker回应中有关键值failure reason(失败原因),就会对应一个人可以读懂的字符串信息解释质询失败的原因,不需要其它关键值。否则,回应必须有两个关键值:interval (间隔)对应下载者定期发出请求的间隔秒数;peers,peer自选ID,IP地址或DNS主机名的字符串和端口号。记住peers不会完全按照计划的间隔发送请求,假如他们发生一个事件或者想要更多的peers。 如果你想对元信息文件或者Tracker质询进行扩展,请与Bram Cohen进行协调,确保所有扩展都兼容。 BitTorrent peer协议通过TCP协议进行操作。它不用调节任何socket选项就可以流畅运行。 peer之间的连接是对称的。两个方向送出的信息要协调一致,数据可以流入任一方。 peer协议指一个peer从零开始下载,每得到元信息文件索引中所描述的一个块且验证码一致,就向所有peer声明已得到此块。 连接的两个终端有2个状态指标,被阻塞与否,被关注与否,被阻塞(choking)是表明在恢复通畅之前数据不再发出的通知。发生阻塞的原因和技术问题稍后会提到。 数据传输发生在一方关注对方且对方没有阻塞的情况下。关注状态必须一致保持-如果一个没阻塞的peer没有别人需要的数据,别人对他就会失去关注,转而关注那些正在阻塞的peer。完全执行这种条件需要非常慎重,但这样的确可以让下载者知道哪些peer在阻塞消失后可以马上开始下载。 连接会逐渐断开不感兴趣和阻塞的peer。 当数据传输时,下载者要备好多份请求排成队列,以获得较高的TCP传输效率(这叫“管运请求”)。另一方面,不能被写入TCP缓冲区的请求要被立即排入内存,而不是一个应用程序级的网络缓冲,一旦阻塞出现,这些请求全部丢弃。 peer连线协议包括一次握手跟着不断的大小一致且确定的信息流。握手的开始是字符十九(十进制),跟着是字符串'BitTorrentprotocol'。开头的字符是长度固定的,希望其它新协议也能这样以便区分。 此后所有送入协议的整数都编码为4字节大中止端。 在现有的应用中头部数据之后是8个全部预留为0的字节,若果你想通过改变这8个预留字节以扩展协议,请与Bram Cohen协调以保证所有扩展兼容。 然后是来自元信息文件中B编码的info值中长20字节的SHA1验证码(和info_hash向Tracker声明的值相同,但这里是原始值那里是引用)。如果双方的值不同,连接断开。一个例外是下载者想只用一个端口进行多个连接下载,它们会先从接入连接得到一个验证码,然后和列表里面的对照,有相同的就答复。 验证码之后是20字节的peer id,它包含在Tracker回应的peer列表中,在向Tracker的请求中被报告。如果接受方peer id不符合发送方希望,连接断开。 握手完毕。之后是长度固定的交互信息流。零长度信息用来保持连接,被忽略。这种信息一般2分钟发出一次,但是在等待数据期间很容易超时。 所有非保持连接用信息开头的字节给出类型,可能值如下: ·0-阻塞 ·1-通畅 ·2-关注 ·3-不关注 ·4-有 ·5-比特组 ·6-请求 ·7-块 ·8-取消 “阻塞”、“通畅”、“关注”和“不关注”类信息没有荷载。 “比特组”类信息仅作为首信息发出。它负载一个比特组,下载者有索引的设为1,其它为0。开始下载时没有任何数据的下载者跳过“比特组”信息。首字节高位到低位对应索引0-7,依次类推,第二字节对应8-15,等等。尾部的剩余的比特位设为0。 “已有”类信息负载一个数,即刚下载并核对完验证码的索引数。 “请求”类信息包括包含一个索引,开始和长度。后两者是字节偏移。长度一般是2的权值除非被文件尾截断。现行一般是2的15次幂,并且关闭大于2的17次幂长度的连接。 “取消”类信息负载和“请求”类信息有一样的负载。它通常在下载接近完成即“最后阶段”发出。当下载快要完成时,剩下几个块有都从同一个线程下载的趋向,这样会很慢。为了确保剩余块下载迅速,一旦还没有决定剩余块的下载请求向谁发出,先向所有他正在从对方下载数据的连接者发送要求所有剩余块的请求。为避免低效,每当一个块开始下载就向其他peer发出取消信息。 “块”类信息包含一个索引,开始和块。记住它和“请求”类信息是相关的。当传输速度很慢或“阻塞”“通畅”类信息高频率交替发出或两者同时发生,可能会载到一个不需要的块。 下载者下载块的顺序是随机的,这样适当防止下载者与其他Peers仅有相同的块子集或超集。 阻塞的发生有很多原因。TCP协议的信息拥挤控制在即时向多连接发送信息的过程中表现极差。同时,阻塞的存在使下载者们能够用以牙还牙式的算法来确保稳定的下载速率。 下面描述的阻塞算法是目前基础的配置。重要的是所有新算法不光要在包含全部扩展算法的网络中运行良好,也要在主要包含这个基础算法的网络中运行良好。 一个优秀的阻塞算法有许多标准。它必须封一定同时上传的数量以获得良好的TCP表现,还要避免频繁的堵塞和通畅交替,即所谓“纤维化”。它应该用数据交换报答给自己数据的peer。最后,它还应该偶尔尝试一下与未使用过的peer端连接,找出比现有连接好的连接,这叫做尝试性疏通。 现行的阻塞算法避免纤维化的手段是每10秒转换被阻塞的名单。疏通4个自己关注且能从他们身上得到最高下载速率的peer,进行上传和数据交换。有较高上传速率但是不被关注下载者的peer被疏通,一旦这些peer开始被关注,那些上传率最低的peer的就被阻塞。如果下载者有了完整的文件,他用自己的上传率而不是下载率来决定疏通谁的连接。 在尝试性疏通中,任何一次中都有一个peer被疏通不管他的上传率如何(如果被关注,他会成为4个提供下载的peer之一)。被尝试性疏通的这种peer每30秒轮换一次。为了给它们一个上传整一个块的机会,新连接会以轮换中尝试性疏通次数的3倍开始连接。 BitTorrent 协议规范(BT协议集合)三 Bittorrent udp-tracker protocol extension Contents introduction connecting Client sends packet: Server replies with packet: announcing Client sends packet: Server replies with packet: scraping Client sends packet: Server replies with packet: errors server replies packet: actions extensions authentication credits introduction A tracker with the protocol "udp://" in its URI is supposed to be contacted using this protocol. This protocol is supported by xbt-tracker. For additional information and descritptions of the terminology used in this document, see the protocol specification All values are sent in network byte order (big endian). The sizes are specified with ANSI-C standard types. If no response to a request is received within 15 seconds, resend the request. If no reply has been received after 60 seconds, stop retrying. connecting Client sends packet:size name description int64_t connection_id Must be initialized to 0x41727101980 in network byte order. This will identify the protocol. int32_t action 0 for a connection request int32_t transaction_id Randomized by client. Server replies with packet:size name description int32_t action Describes the type of packet, in this case it should be 0, for connect. If 3 (for error) see errors. int32_t transaction_id Must match the transaction_id sent from the client. int64_t connection_id A connection id, this is used when further information is exchanged with the tracker, to identify you. This connection id can be reused for multiple requests, but if it's cached for too long, it will not be valid anymore. announcing Client sends packet:size name description int64_t connection_id The connection id acquired from establishing the connection. int32_t action Action. in this case, 1 for announce. See actions. int32_t transaction_id Randomized by client. int8_t[20] info_hash The info-hash of the torrent you want announce yourself in. int8_t[20] peer_id Your peer id. int64_t downloaded The number of byte you've downloaded in this session. int64_t left The number of bytes you have left to download until you're finished. int64_t uploaded The number of bytes you have uploaded in this session. int32_t event The event, one of none = 0 completed = 1 started = 2 stopped = 3 uint32_t ip Your ip address. Set to 0 if you want the tracker to use the sender of this udp packet. uint32_t key A unique key that is randomized by the client. int32_t num_want The maximum number of peers you want in the reply. Use -1 for default. uint16_t port The port you're listening on. uint16_t extensions See extensions Server replies with packet:size name description int32_t action The action this is a reply to. Should in this case be 1 for announce. If 3 (for error) see errors. See actions. int32_t transaction_id Must match the transaction_id sent in the announce request. int32_t interval the number of seconds you should wait until reannouncing yourself. int32_t leechers The number of peers in the swarm that has not finished downloading. int32_t seeders The number of peers in the swarm that has finished downloading and are seeding. The rest of the server reply is a variable number of the following structure: size name description int32_t ip The ip of a peer in the swarm. uint16_t port The peer's listen port. scraping Client sends packet:size name description int64_t connection_id The connection id retreived from the establishing of the connection. int32_t action The action, in this case, 2 for scrape. See actions. int32_t transaction_id Randomized by client. int16_t num_info_hashes The number of info-hashes that will follow. uint16_t extensions See extensions. The following structure is repeated num_info_hashes times: size name description int8_t[20] info_hash The info hash that is to be scraped. Server replies with packet:size name description int32_t action The action, should in this case be 2 for scrape. If 3 (for error) see errors. int32_t transaction_id Must match the sent transaction id. The rest of the packet contains the following structures once for each info-hash you asked in the scrape request. size name description int32_t complete The total number of completed downloads. int32_t downloaded The current number of connected seeds. int32_t incomplete The current number of connected leechers. errors In case of a tracker error, server replies packet:size name description int32_t action The action, in this case 3, for error. See actions. int32_t transaction_id Must match the transaction_id sent from the client. int8_t[] error_string The rest of the packet is a string describing the error. actions The action fields has the following encoding: connect = 0 announce = 1 scrape = 2 error = 3 (only in server replies) extensions The extensions field is a bitmask. The following bits are assigned: 1 = authentication. authenticationThe packet will have an authentication part appended to it. It has the following format: size name description int8_t username_length The number of characters in the username. int8_t[] username The username, the number of characters as specified in the previous field. uint8_t[8] passwd_hash sha1(packet + sha1(password)) The packet in this case means the entire packet except these 8 bytes that are the password hash. These are the 8 first bytes (most significant) from the 20 bytes hash calculated. credits Protocol designed by Olaf van der Spek BitTorrent 协议规范(BT协议集合)四 通常BT客户端每几分钟就要向tracker发送一次请求.对于一些比较大的BT站点,其tracker的压力是可想而知的.降低tracker的压力首先考虑到的当然是采用更低网络开销的udp协议.于是Bittorrent udp-tracker protocol应运而生. 这个协议很简单. 下面是实现它的封装类: // UDPTrackerClient.h: interface for the CUDPTrackerClient class. // // #if !defined(AFX_UDPTRACKERCLIENT_H__69B6ACC8_8193_4680_81D8_925B1550E92C__INCLUDED_) #define AFX_UDPTRACKERCLIENT_H__69B6ACC8_8193_4680_81D8_925B1550E92C__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 #include <WINSOCK2.H> #pragma comment(lib, "ws2_32.lib") #ifndef _DISABLEWARNING4786_4355 #define _DISABLEWARNING4786_4355 #pragma warning( disable : 4786 ) #pragma warning( disable : 4355 ) #endif #ifndef _ENABLEUSESTL #define _ENABLEUSESTL #include <LIST> #include <SET> #include <VECTOR> #include <QUEUE> #include <STRING> #include <MAP> using namespace std; #endif class CPeerHostInfo { public: DWORD IP;//节点IP WORD Port;//节点端口 }; class CUDPTrackerClient { public: CUDPTrackerClient(); virtual ~CUDPTrackerClient(); void CancelSocketOperate(); BOOL Connect(const char * szServer,WORD wPort = 0); DWORD Announcing(BYTE* pInfoHash,BYTE * pPeerID, __int64 idownloaded,__int64 ileft,__int64 iuploaded, int ievent, DWORD dwIP,WORD wPort); BOOL Disconnect(); public: SOCKET m_socket; DWORD m_dwIP; WORD m_wPort; __int64 m_iConnection_id; DWORD m_dwConnectTick; string m_strError; //如果请求失败,此变量保存错误信息 DWORD m_dwDonePeers; //种子数 DWORD m_dwNumPeers; //当前下载者个数 DWORD m_dwInterval; //查询间隔时间 list m_listPeers; }; #endif // !defined(AFX_UDPTRACKERCLIENT_H__69B6ACC8_8193_4680_81D8_925B1550E92C__INCLUDED_) // UDPTrackerClient.cpp: implementation of the CUDPTrackerClient class. // // #include "stdafx.h" #include "UDPTrackerClient.h" #include "DataStream.h" #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif // // Construction/Destruction // #define RECVBUFSIZE 2048 CUDPTrackerClient::CUDPTrackerClient() { m_socket = INVALID_SOCKET; m_iConnection_id = 0; m_dwConnectTick = 0; m_dwIP = 0; m_wPort = 0; m_dwDonePeers = 0; //种子数 m_dwNumPeers = 0; //当前下载者个数 m_dwInterval = 0; //查询间隔时间 } CUDPTrackerClient::~CUDPTrackerClient() { Disconnect(); } void CUDPTrackerClient::CancelSocketOperate() { if(m_socket != INVALID_SOCKET) { LINGER lingerStruct; // If we're supposed to abort the connection, set the linger value // on the socket to 0. lingerStruct.l_onoff = 1; lingerStruct.l_linger = 0; setsockopt(m_socket, SOL_SOCKET, SO_LINGER, (char *)&lingerStruct, sizeof(lingerStruct) ); } } BOOL CUDPTrackerClient::Disconnect() { m_iConnection_id = 0; m_dwDonePeers = 0; //种子数 m_dwNumPeers = 0; //当前下载者个数 m_dwInterval = 0; //查询间隔时间 if ( m_socket != INVALID_SOCKET ) { m_dwIP = 0; m_wPort = 0; // Now close the socket handle. This will do an abortive or // graceful close, as requested. shutdown(m_socket,SD_BOTH); closesocket(m_socket); m_socket = INVALID_SOCKET; return TRUE; } return FALSE; } //szServer连接的主机,可以是下列形式的字符串: //easeso.com:1000 //easeso.com //如果wPort不为0,则szServer不应该包含端口信息 BOOL CUDPTrackerClient::Connect(const char * szServer,WORD wPort) { m_strError = ""; BOOL bRes = FALSE; if ( m_socket == INVALID_SOCKET ) { //用UDP初始化套接字 BOOL optval = TRUE; m_socket =socket(AF_INET,SOCK_DGRAM,0); if(m_socket == INVALID_SOCKET) return FALSE; //设置超时时间 int TimeOut=10000; int err = setsockopt (m_socket, SOL_SOCKET,SO_RCVTIMEO,(CHAR *) &TimeOut,sizeof (TimeOut)); } if(m_dwIP == 0) { CString strServer = szServer; CString strHost; if(wPort == 0) { int iNext = strServer.Find(':'); if(iNext>0) { strHost = strServer.Mid(0,iNext); CString strPort = strServer.Mid(iNext+1); m_wPort = (WORD)atoi(strPort); } else strHost = strServer; } else { strHost = strServer; m_wPort = wPort; } if(m_wPort == 0) m_wPort = 80; //Check if address is an IP or a Domain Name int a = strHost[0]; if (a > 47 && a < 58) m_dwIP = inet_addr(strHost); else { struct hostent *pHost; pHost = gethostbyname(strHost); if(pHost != NULL) m_dwIP = *((ULONG*)pHost->h_addr); else m_dwIP = 0; } } if((GetTickCount()-m_dwConnectTick)>30000) { m_dwConnectTick = 0; m_iConnection_id = 0; } if(m_socket != INVALID_SOCKET && m_dwIP && m_wPort && m_iConnection_id ==0) { DWORD dwTransaction_id = GetTickCount(); SOCKADDR_IN from; int fromlength=sizeof(SOCKADDR); char buf[RECVBUFSIZE]; from.sin_family=AF_INET; from.sin_addr.s_addr=m_dwIP; from.sin_port=htons(m_wPort); CDataStream sendstream(buf,2047); sendstream.clear(); __int64 iCID = 0x41727101980; sendstream.writeint64(CNetworkByteOrder::convert(iCID)); sendstream.writedword(CNetworkByteOrder::convert((int)0)); sendstream.writedword(dwTransaction_id); int iRes = 0; int iTimes = 6; while(iTimes>0&&m_dwIP) { sendto(m_socket,sendstream.getbuffer(),sendstream.size(),0,(struct sockaddr FAR *)&from,sizeof(from)); iRes = recvfrom(m_socket,buf,RECVBUFSIZE-1,0,(struct sockaddr FAR *)&from,(int FAR *)&fromlength); if(iRes >=0) break; iTimes--; } if(iRes>=16) { CDataStream recvstream(buf,RECVBUFSIZE-1); DWORD dwAction = (DWORD)CNetworkByteOrder::convert((int)recvstream.readdword()); DWORD dwTIDResp= recvstream.readdword(); if(dwTIDResp == dwTransaction_id) { if(dwAction == 0) { m_iConnection_id = recvstream.readint64(); //BitComet将回复0x16字节数据,最后6字节是服务器查看到的本地IP和UDP端口 } else if(dwAction == 3)//得到一个错误信息包 { buf[iRes]=0; m_strError = recvstream.readstring(); } } } } if(m_iConnection_id) bRes = TRUE; return bRes; } //提交请求 //pInfoHash 20字节的数据缓冲区指针 //pPeerID 20字节的数据缓冲区指针 //ievent参数值: //none = 0 //completed = 1 //started = 2 //stopped = 3 DWORD CUDPTrackerClient::Announcing(BYTE* pInfoHash,BYTE * pPeerID, __int64 idownloaded,__int64 ileft,__int64 iuploaded, int ievent, DWORD dwIP,WORD wPort) { m_listPeers.clear(); m_dwNumPeers = 0; m_dwDonePeers = 0; m_strError = ""; DWORD dwReturnCode = 0; if(m_iConnection_id && m_socket != INVALID_SOCKET && m_dwIP & m_wPort) { DWORD dwTransaction_id = GetTickCount(); //srand(dwTransaction_id); //DWORD dwKey = rand(); DWORD dwKey = 0x3753; SOCKADDR_IN from; int fromlength=sizeof(SOCKADDR); char buf[RECVBUFSIZE]; from.sin_family=AF_INET; from.sin_addr.s_addr=m_dwIP; from.sin_port=htons(m_wPort); CDataStream sendstream(buf,RECVBUFSIZE-1); sendstream.clear(); sendstream.writeint64(m_iConnection_id); sendstream.writedword(CNetworkByteOrder::convert((int)1)); sendstream.writedword(dwTransaction_id); sendstream.writedata(pInfoHash,20); sendstream.writedata(pPeerID,20); sendstream.writeint64(CNetworkByteOrder::convert(idownloaded)); sendstream.writeint64(CNetworkByteOrder::convert(ileft)); sendstream.writeint64(CNetworkByteOrder::convert(iuploaded)); sendstream.writedword(CNetworkByteOrder::convert(ievent)); sendstream.writedword(dwIP); sendstream.writedword(CNetworkByteOrder::convert((int)dwKey)); sendstream.writedword(CNetworkByteOrder::convert((int)200)); sendstream.writedword(CNetworkByteOrder::convert(wPort)); int iRes = 0; int iTimes = 2; while(iTimes>0&&m_dwIP) { sendto(m_socket,sendstream.getbuffer(),sendstream.size(),0,(struct sockaddr FAR *)&from,sizeof(from)); iRes = recvfrom(m_socket,buf,RECVBUFSIZE-1,0,(struct sockaddr FAR *)&from,(int FAR *)&fromlength); if(iRes >=0) break; iTimes--; } if(iRes>=20) { CDataStream recvstream(buf,RECVBUFSIZE-1); DWORD dwAction = (DWORD)CNetworkByteOrder::convert((int)recvstream.readdword()); DWORD dwTIDResp= recvstream.readdword(); if(dwTIDResp == dwTransaction_id) { if(dwAction == 1) { m_dwInterval = (DWORD)CNetworkByteOrder::convert((int)recvstream.readdword()); m_dwNumPeers = (DWORD)CNetworkByteOrder::convert((int)recvstream.readdword()); m_dwDonePeers = (DWORD)CNetworkByteOrder::convert((int)recvstream.readdword()); CPeerHostInfo hi; for(int iCurPos = 20;iCurPos+6<=iRes;iCurPos+=6) { hi.IP= recvstream.readdword(); hi.Port = (WORD)CNetworkByteOrder::convert((unsigned short)recvstream.readword()); m_listPeers.push_back(hi); } if(m_dwNumPeers>m_listPeers.size()) { iRes = 0; iTimes = 6; while(iTimes>0&&m_dwIP) { iRes = recvfrom(m_socket,buf,RECVBUFSIZE-1,0,(struct sockaddr FAR *)&from,(int FAR *)&fromlength); if(iRes >=0) break; iTimes--; } if(iRes>=6) { for(iCurPos = 0;iCurPos+6<=iRes;iCurPos+=6) { hi.IP= recvstream.readdword(); hi.Port = (DWORD)CNetworkByteOrder::convert((int)recvstream.readword()); m_listPeers.push_back(hi); } } } m_dwNumPeers = m_listPeers.size(); dwReturnCode = 200; } else if(dwAction == 3)//得到一个错误信息包 { buf[iRes]=0; m_strError = recvstream.readstring(); dwReturnCode = 400; } } } } //每次都要求重新连接 m_iConnection_id = 0; return dwReturnCode; } // DataStream.h: interface for the CDataStream class. // // #if !defined(AFX_DATASTREAM_H__D90A2534_EA73_4BEA_8B7E_87E59A3D1D26__INCLUDED_) #define AFX_DATASTREAM_H__D90A2534_EA73_4BEA_8B7E_87E59A3D1D26__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 #include //数据流操作函数 class CDataStream { public : CDataStream(char * szBuf,int isize) { m_isize = isize; buffer = szBuf; current = buffer; } ~CDataStream() { } void clear() { current = buffer; current[0]=0; } //此函数不动态增加内存,一次打印的数据长度不应该超过缓冲区的三分之一,否则可能导致失败 bool printf(const char * format,...) { if(current) { if(current - buffer > (m_isize*2)/3) return false; va_list argPtr ; va_start( argPtr, format ) ; int count = vsprintf( current, format, argPtr ) ; va_end( argPtr ); current += count ; return true; } return false; } //此函数拷贝字符串 bool strcpy(const char * szStr) { if(current&&szStr) { int ilen = lstrlen(szStr); if((m_isize-(current - buffer)) < (ilen +2)) return false; memcpy(current,szStr,ilen+1); current += ilen; return true; } return false; } char * getcurrentpos() { return current; } void move(int ilen)//当前指针向后移动ilen { current += ilen; } void reset() { current = buffer; } BYTE readbyte() { current ++; return *(current-1); } void writebyte(BYTE btValue) { *current = btValue; current ++; } WORD readword() { current +=2; return *((WORD*)(current-2)); } void writeword(WORD wValue) { *((WORD*)current) = wValue; current +=2; } DWORD readdword() { current +=4; return *((DWORD*)(current-4)); } void writedword(DWORD dwValue) { *((DWORD*)current) = dwValue; current +=4; } __int64 readint64() { current +=8; return *((__int64*)(current-8)); } void writeint64(__int64 iValue) { *((__int64*)current) = iValue; current +=8; } BYTE * readdata(DWORD dwLen) { current +=dwLen; return (BYTE*)(current-dwLen); } void writedata(BYTE * pData,DWORD dwLen) { memcpy(current,pData,dwLen); current +=dwLen; } char * readstring() { char * szRes = current; int ilen = lstrlen(current); current +=(ilen+1); return szRes; } int size() { return (int)(current-buffer); } const char * getbuffer(){return buffer;} private : char* buffer; char* current; int m_isize; }; class CNetworkByteOrder { public: static unsigned short int convert(unsigned short int iValue) { unsigned short int iData; ((BYTE*)&iData)[0] = ((BYTE*)&iValue)[1]; ((BYTE*)&iData)[1] = ((BYTE*)&iValue)[0]; return iData; } static int convert(int iValue) { int iData; ((BYTE*)&iData)[0] = ((BYTE*)&iValue)[3]; ((BYTE*)&iData)[1] = ((BYTE*)&iValue)[2]; ((BYTE*)&iData)[2] = ((BYTE*)&iValue)[1]; ((BYTE*)&iData)[3] = ((BYTE*)&iValue)[0]; return iData; } static __int64 convert(__int64 iValue) { __int64 iData; ((BYTE*)&iData)[0] = ((BYTE*)&iValue)[7]; ((BYTE*)&iData)[1] = ((BYTE*)&iValue)[6]; ((BYTE*)&iData)[2] = ((BYTE*)&iValue)[5]; ((BYTE*)&iData)[3] = ((BYTE*)&iValue)[4]; ((BYTE*)&iData)[4] = ((BYTE*)&iValue)[3]; ((BYTE*)&iData)[5] = ((BYTE*)&iValue)[2]; ((BYTE*)&iData)[6] = ((BYTE*)&iValue)[1]; ((BYTE*)&iData)[7] = ((BYTE*)&iValue)[0]; return iData; } }; #endif // !defined(AFX_DATASTREAM_H__D90A2534_EA73_4BEA_8B7E_87E59A3D1D26__INCLUDED_) BitTorrent 协议规范(BT协议集合)五 BitTorrent is a protocol for distributing files. It identifies content by URL and is designed to integrate seamlessly with the web. Its advantage over plain HTTP is that when multiple downloads of the same file happen concurrently, the downloaders upload to each other, making it possible for the file source to support very large numbers of downloaders with only a modest increase in its load. A BitTorrent file distribution consists of these entities: An ordinary web server A static 'metainfo' file A BitTorrent tracker An 'original' downloader The end user web browsers The end user downloaders There are ideally many end users for a single file. To start serving, a host goes through the following steps: Start running a tracker (or, more likely, have one running already). Start running an ordinary web server, such as apache, or have one already. Associate the extension .torrent with mimetype application/x-bittorrent on their web server (or have done so already). Generate a metainfo (.torrent) file using the complete file to be served and the URL of the tracker. Put the metainfo file on the web server. Link to the metainfo (.torrent) file from some other web page. Start a downloader which already has the complete file (the 'origin'). To start downloading, a user does the following: Install BitTorrent (or have done so already). Surf the web. Click on a link to a .torrent file. Select where to save the file locally, or select a partial download to resume. Wait for download to complete. Tell downloader to exit (it keeps uploading until this happens). The connectivity is as follows: The web site is serving up static files as normal, but kicking off the BitTorrent helper app on the clients. The tracker is receiving information from all downloaders and giving them random lists of peers. This is done over HTTP or HTTPS. Downloaders are periodically checking in with the tracker to keep it informed of their progress, and are uploading to and downloading from each other via direct connections. These connections use the BitTorrent peer protocol, which operates over TCP. The origin is uploading but not downloading at all, since it has the entire file. The origin is necessary to get the entire file into the network. Often for popular downloads the origin can be taken down after a while since several downloads may have completed and been left running indefinitely. Metainfo file and tracker responses are both sent in a simple, efficient, and extensible format called bencoding (pronounced 'bee encoding'). Bencoded messages are nested dictionaries and lists (as in Python), which can contain strings and integers. Extensibility is supported by ignoring unexpected dictionary keys, so additional optional ones can be added later. Bencoding is done as follows: Strings are length-prefixed base ten followed by a colon and the string. For example 4:spam corresponds to 'spam'. Integers are represented by an 'i' followed by the number in base 10 followed by an 'e'. For example i3e corresponds to 3 and i-3e corresponds to -3. Integers have no size limitation. i-0e is invalid. All encodings with a leading zero, such as i03e, are invalid, other than i0e, which of course corresponds to 0. Lists are encoded as an 'l' followed by their elements (also bencoded) followed by an 'e'. For example l4:spam4:eggse corresponds to ['spam', 'eggs']. Dictionaries are encoded as a 'd' followed by a list of alternating keys and their corresponding values followed by an 'e'. For example, d3:cow3:moo4:spam4:eggse corresponds to {'cow': 'moo', 'spam': 'eggs'} and d4:spaml1:a1:bee corresponds to {'spam': ['a', 'b']} . Keys must be strings and appear in sorted order (sorted as raw strings, not alphanumerics). Metainfo files are bencoded dictionaries with the following keys: announce The URL of the tracker. info This maps to a dictionary, with keys described below. The name key maps to a string which is the suggested name to save the file (or directory) as. It is purely advisory. piece length maps to the number of bytes in each piece the file is split into. For the purposes of transfer, files are split into fixed-size pieces which are all the same length except for possibly the last one which may be truncated. Piece length is almost always a power of two, most commonly 218 = 256 K (BitTorrent prior to version 3.2 uses 220 = 1 M as default). pieces maps to a string whose length is a multiple of 20. It is to be subdivided into strings of length 20, each of which is the SHA1 hash of the piece at the corresponding index. There is also a key length or a key files, but not both or neither. If length is present then the download represents a single file, otherwise it represents a set of files which go in a directory structure. In the single file case, length maps to the length of the file in bytes. For the purposes of the other keys, the multi-file case is treated as only having a single file by concatenating the files in the order they appear in the files list. The files list is the value files maps to, and is a list of dictionaries containing the following keys: length The length of the file, in bytes. path A list of strings corresponding to subdirectory names, the last of which is the actual file name (a zero length list is an error case). In the single file case, the name key is the name of a file, in the muliple file case, it's the name of a directory. Tracker queries are two way. The tracker receives information via HTTP GET parameters and returns a bencoded message. Note that although the current tracker implementation has its own web server, the tracker could run very nicely as, for example, an apache module. Tracker GET requests have the following keys: info_hash The 20 byte sha1 hash of the bencoded form of the info value from the metainfo file. Note that this is a substring of the metainfo file. This value will almost certainly have to be escaped. peer_id A string of length 20 which this downloader uses as its id. Each downloader generates its own id at random at the start of a new download. This value will also almost certainly have to be escaped. ip An optional parameter giving the IP (or dns name) which this peer is at. Generally used for the origin if it's on the same machine as the tracker. port The port number this peer is listening on. Common behavior is for a downloader to try to listen on port 6881 and if that port is taken try 6882, then 6883, etc. and give up after 6889. uploaded The total amount uploaded so far, encoded in base ten ascii. downloaded The total amount downloaded so far, encoded in base ten ascii. left The number of bytes this peer still has to download, encoded in base ten ascii. Note that this can't be computed from downloaded and the file length since it might be a resume, and there's a chance that some of the downloaded data failed an integrity check and had to be re-downloaded. event This is an optional key which maps to started, completed, or stopped (or empty, which is the same as not being present). If not present, this is one of the announcements done at regular intervals. An announcement using started is sent when a download first begins, and one using completed is sent when the download is complete. No completed is sent if the file was complete when started. Downloaders send an announcement using 'stopped' when they cease downloading. Tracker responses are bencoded dictionaries. If a tracker response has a key failure reason, then that maps to a human readable string which explains why the query failed, and no other keys are required. Otherwise, it must have two keys: interval, which maps to the number of seconds the downloader should wait between regular rerequests, and peers. peers maps to a list of dictionaries corresponding to peers, each of which contains the keys peer id, ip, and port, which map to the peer's self-selected ID, IP address or dns name as a string, and port number, respectively. Note that downloaders may rerequest on nonscheduled times if an event happens or they need more peers. If you want to make any extensions to metainfo files or tracker queries, please coordinate with Bram Cohen to make sure that all extensions are done compatibly. BitTorrent's peer protocol operates over TCP. It performs efficiently without setting any socket options. Peer connections are symmetrical. Messages sent in both directions look the same, and data can flow in either direction. The peer protocol refers to pieces of the file by index as described in the metainfo file, starting at zero. When a peer finishes downloading a piece and checks that the hash matches, it announces that it has that piece to all of its peers. Connections contain two bits of state on either end: choked or not, and interested or not. Choking is a notification that no data will be sent until unchoking happens. The reasoning and common techniques behind choking are explained later in this document. Data transfer takes place whenever one side is interested and the other side is not choking. Interest state must be kept up to date at all times - whenever a downloader doesn't have something they currently would ask a peer for in unchoked, they must express lack of interest, despite being choked. Implementing this properly is tricky, but makes it possible for downloaders to know which peers will start downloading immediately if unchoked. Connections start out choked and not interested. When data is being transferred, downloaders should keep several piece requests queued up at once in order to get good TCP performance (this is called 'pipelining'.) On the other side, requests which can't be written out to the TCP buffer immediately should be queued up in memory rather than kept in an application-level network buffer, so they can all be thrown out when a choke happens. The peer wire protocol consists of a handshake followed by a never-ending stream of length-prefixed messages. The handshake starts with character ninteen (decimal) followed by the string 'BitTorrent protocol'. The leading character is a length prefix, put there in the hope that other new protocols may do the same and thus be trivially distinguishable from each other. All later integers sent in the protocol are encoded as four bytes big-endian. After the fixed headers come eight reserved bytes, which are all zero in all current implementations. If you wish to extend the protocol using these bytes, please coordinate with Bram Cohen to make sure all extensions are done compatibly. Next comes the 20 byte sha1 hash of the bencoded form of the info value from the metainfo file. (This is the same value which is announced as info_hash to the tracker, only here it's raw instead of quoted here). If both sides don't send the same value, they sever the connection. The one possible exception is if a downloader wants to do multiple downloads over a single port, they may wait for incoming connections to give a download hash first, and respond with the same one if it's in their list. After the download hash comes the 20-byte peer id which is reported in tracker requests and contained in peer lists in tracker responses. If the receiving side's peer id doesn't match the one the initiating side expects, it severs the connection. That's it for handshaking, next comes an alternating stream of length prefixes and messages. Messages of length zero are keepalives, and ignored. Keepalives are generally sent once every two minutes, but note that timeouts can be done much more quickly when data is expected. All non-keepalive messages start with a single byte which gives their type. The possible values are: 0 - choke 1 - unchoke 2 - interested 3 - not interested 4 - have 5 - bitfield 6 - request 7 - piece 8 - cancel 'choke', 'unchoke', 'interested', and 'not interested' have no payload. 'bitfield' is only ever sent as the first message. Its payload is a bitfield with each index that downloader has sent set to one and the rest set to zero. Downloaders which don't have anything yet may skip the 'bitfield' message. The first byte of the bitfield corresponds to indices 0 - 7 from high bit to low bit, respectively. The next one 8-15, etc. Spare bits at the end are set to zero. The 'have' message's payload is a single number, the index which that downloader just completed and checked the hash of. 'request' messages contain an index, begin, and length. The last two are byte offsets. Length is generally a power of two unless it gets truncated by the end of the file. All current implementations use 215, and close connections which request an amount greater than 217. 'cancel' messages have the same payload as request messages. They are generally only sent towards the end of a download, during what's called 'endgame mode'. When a download is almost complete, there's a tendency for the last few pieces to all be downloaded off a single hosed modem line, taking a very long time. To make sure the last few pieces come in quickly, once requests for all pieces a given downloader doesn't have yet are currently pending, it sends requests for everything to everyone it's downloading from. To keep this from becoming horribly inefficient, it sends cancels to everyone else every time a piece arrives. 'piece' messages contain an index, begin, and piece. Note that they are correlated with request messages implicitly. It's possible for an unexpected piece to arrive if choke and unchoke messages are sent in quick succession and/or transfer is going very slowly. Downloaders generally download pieces in random order, which does a reasonably good job of keeping them from having a strict subset or superset of the pieces of any of their peers. Choking is done for several reasons. TCP congestion control behaves very poorly when sending over many connections at once. Also, choking lets each peer use a tit-for-tat-ish algorithm to ensure that they get a consistent download rate. The choking algorithm described below is the currently deployed one. It is very important that all new algorithms work well both in a network consisting entirely of themselves and in a network consisting mostly of this one. There are several criteria a good choking algorithm should meet. It should cap the number of simultaneous uploads for good TCP performance. It should avoid choking and unchoking quickly, known as 'fibrillation'. It should reciprocate to peers who let it download. Finally, it should try out unused connections once in a while to find out if they might be better than the currently used ones, known as optimistic unchoking. The currently deployed choking algorithm avoids fibrillation by only changing who's choked once every ten seconds. It does reciprocation and number of uploads capping by unchoking the four peers which it has the best download rates from and are interested. Peers which have a better upload rate but aren't interested get unchoked and if they become interested the worst uploader gets choked. If a downloader has a complete file, it uses its upload rate rather than its download rate to decide who to unchoke. For optimistic unchoking, at any one time there is a single peer which is unchoked regardless of it's upload rate (if interested, it counts as one of the four allowed downloaders.) Which peer is optimistically unchoked rotates every 30 seconds. To give them a decent chance of getting a complete piece to upload, new connections are three times as likely to start as the current optimistic unchoke as anywhere else in the rotation. BitTorrent 协议规范(BT协议集合)六 BT是如何采用激励机制来达到健壮性的 Bram Cohen bram@bitconjurer.org 2003年5月22日 翻译:小马哥 日期:2004-6-1 修改:扬帆 日期:2005-5-9 概要 BitTorrent 文件发布系统采用针锋相对(tit_for_tat)的方法来达到帕累托有效,与当前已知的协作技术相比,它具有更高的活力。本文将解释BitTorrent 的用途,以及是怎样用经济学的方法来达到这个目标的。 1、BitTorrent 用来做什么? 当通过HTTP协议来下载一个文件的时候,所有的上载开销都在主机上。而使用 BitTorrent,当多个人同时下载同一个文件的时候,他们之间也相互为对方提供文件的部分片断的下载。这样,就把上载的开销分摊到每个下载者那里,也就可以在理论上支持无限多个下载者来下载同一个文件。 研究人员以前也在寻找一种达到这种效果的可实用的技术[3]。这种技术原来并没有在大的范围内运用过,因为逻辑和的问题非常棘手。如果仅仅计算哪些 peers 拥有文件的哪些片断以及这些片断应该被发送给谁,那么很难只产生比较小的系统开销。Peers之间的连接很少会超过几个小时,通常是几分钟而已。最后,有一个普遍的问题,就是公平性。 我们将解释BitTorrent 是如何很好的解决这些问题的。 1.1、BitTorrent接口 BitTorrent 的接口可能是最简单的。用户点击希望下载的文件的超级链接,然后会弹出一个标准的“保存到”对话框。此后,出现一个下载进度的窗口,在这个窗口中,除了显示下载速率外,还显示一个上载速率。BT在使用上非常简单,使得BT能广泛的被运用。 1.2、部署 决定采用BitTorrent的原因是因为有一些文件需要发布。而下载者使用 BitTorrent,是因为这是他们获取所需要的文件的唯一途径。下载者经常一完成下载,就停止为别人上载,虽然说,在BT 客户端

标签: 风差压变送器zp

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台