钱文翔的博客

(翻译)BitTorrent-Protocol协议规范

本文翻译原文:http://www.bittorrent.org/beps/bep_0003.html
BitTorrent是一个分发文件的协议。它通过URL识别内容,旨在与网络无缝集成。它和一般的HTTP相比的优势在于,当同一个文件的多个下载同时发生时,下载者相互上传,使得文件源可以支持非常大量下载,而其负载增加很小。

BitTorrent文件分发有这些部分组成:

  • 一个普通的web服务器
  • 静态“元信息(metainfo)”文件
  • BitTorrent跟踪器(tracker)
  • “原始”下载器
  • 用户web浏览器端
  • 用户下载器端

理想情况下多端用户现在同一个文件

要开始服务,主机将执行以下步骤

  1. 开启一个跟踪器(tracker)
  2. 开启一个普通的web服务器,例如Apache
  3. 在他们的Web服务器上,将扩展名.torrent与mimetype应用程序/ x-bittorrent相关联。
  4. 使用提供的完整的文件和跟踪器URL,生成一个元数据(metainfo)的(.torrent) 文件
  5. 将元信息(.torrent)文件放在web服务器上
  6. 链接来自其它网页的(.torrent)元数据文件

    bencoding

  • 十进制的字符串长度,后面跟着冒号和字符串。例如4:spam对应于“span”
  • 整数用一个’i’来表示,后跟一个十进制的数字,再后面跟着一个’e’。例如:i3e对应3,i-3e代表-3。整数没有大小限制,i-0e是无效的。所有具有前导0的编码,例如i03e,都是无效的,除了i0e,其毫无疑问对应0。
  • 列表被编码成‘l’,后面是他们的元素(也是bencode),在接着是‘e’.例如:l4:spam4:eggs对应:[‘spam’,’eggs’].
  • 字典被编码成’d’,后面跟着一个交替的建和对应的列表,后跟一个’e’。例如,d3:cow3:moo4:spam4:eggse 对应{‘cow’: ‘moo’, ‘spam’: ‘eggs’}, d4:spaml1:a1:bee对应{‘spam’: [‘a’, ‘b’]}。键必须是字符串并按排序顺序显示(按原始字符串排序,而不是字母数字)。

metainfo files(元信息文件)

元信息文件(也被认作.torrent文件)使用以下键编程成bencoded字典:

  • announce: 跟踪器(tracker)的URL
  • info: 映射到字典,具有如下所述的键。

所有在.torrent文件中包含的字符串必须是UTF-8编码的

信息字典

映射UTF编码的字符串的关键字,是用来保存文件的默认名字。它仅仅用来咨询。
片段(piece)长度对应到文件被分割成的每个片段中的字节数。为了传输的目的,文件被拆分成固定大小的块,它们具有相同的长度,除了可能被截断的最后一个之外。
片(piece)长度几乎总是2的幂,最常见的2 18 = 256 K(BitTorrent在版本3.2之前使用2 20 = 1 M作为默认值)。
它被细分为长度为20的字符串,每个字符串是相应索引处的片段的SHA1哈希。
还有一个密钥长度或一个密钥文件,但不能同时存在或同时不存在。如果长度存在,则下载表示单个文件,否则它表示进入目录结构的一组文件。
在单个文件的情况下,长度映射到文件的长度(以字节为单位)。
为了其他键的目的,多文件情况被视为只有一个文件,通过按照它们出现在文件列表中的顺序连接文件。文件列表是文件映射到的值,是包含以下键的字典列表:

  • length:文件的长度(以字节为单位)。
  • path 与子目录名称对应的UTF-8编码字符串的列表,最后一个是实际文件名(零长度列表是一个错误大小写)。
    在单文件的情况下,名称键是文件的名称,在多文件的情况下,它是目录的名称。

跟踪器(tracker)

跟踪器请求包含以下关键字(key):

  • info_hash
    来自metainfo文件的info值的bencoded形式的20字节sha1散列。注意,这是metainfo文件的子字符串。 info_hash必须是在.torrent文件中找到的编码形式的哈希,而不管它是无效的。这个值肯定必须被转义。
  • peer_id
    是长度为20的字符串,被下载程序用作其id。每个下载程序在新下载开始时随机生成自己的ID。这个值也必须被转义。
  • ip
    是此peer所在的IP(或dns名称)的可选参数。
  • port
    是peer正在侦听的端口号。常见的做法是下载器尝试侦听端口6881,如果该端口被占用,则尝试6882,接着6883等,在尝试6889端口后若仍不成功,则放弃.
  • upload
    目前为止所有的上传量,编码成十进制ascii码。
  • downloaded
    到目前为止已下载的总量,编码成十进制ascii码。
  • left
    peer的剩余下载量,以十进制ascii编码。请注意,这不能从已下载的量和文件长度计算,因为它可能只是一个摘要,并且有可能是,部分已下载的数据的完整性检查失败,必须重新下载。
  • event
    这是一个对应started,completed或stopped 的可选键(或empty,代表不存在)。如果不存在,则定时发出公告。当下载开始时,发送started的通知,并且当下载完成时发送completed。如果文件在启动时已完成,则不会发送completed。下载器在停止下载时发送stopped公告。

跟踪器(Tracker)的响应是经过bencoded编码的字典。如果跟踪器响应具有关键值的失败原因,则对应到人类可读的字符串,解释了为什么查询失败,并且不需要关其他键字(keys)。除此之外,必须有两个关键字:interval,对应下载器在定期重新请求时应该等待多少秒。peers,对应peers相应的字典列表,每一个都包含peer id,ip和port,peer id指的是peer自选ID,ip指的是IP地址或者dns名字,port指的是端口数。注意,如果有事件发生,或者需要更多的peer,下载器可以在未调度的时间重新请求。
更常见的是跟踪器返回peer列表的压缩表示,参见BEP 23
通常也通过UDP跟踪器协议来通告。

对等协议(peer protocal)

BitTorrent的对等协议通过TCP或uTP进行操作。
peer的连接是对称的。在两个方向上发送的消息看起来是相同的,并且数据可以在任一方向上流动。

对等协议指的是,在元信息文件中描述的索引零的文件片段。当peer下载完一个块(piece)并且该散列匹配时,它向其他所有的peer宣告它具有了那个块。
连接在任一端都包含两位状态:阻塞(choked)或者非阻塞,感兴趣(interested)或者不感兴趣。阻塞表示在非阻塞发生前,不会有数据发送。文档后面将解释阻塞的推论和通用技术。

只要一方感兴趣,另一方没有阻塞,就进行数据传输。必须随时保持最新感兴趣状态 - 每当下载者当前没有某些资源时,将会向一个peer询问是否处于非阻塞状态,即使被阻塞。他们也必须表示不感兴趣。正确的实现这个很刺手,但是这可以下载者知道,如果不阻塞,哪一个peer将会开始下载。

连接开始,就将状态变为被阻塞和不感兴趣。

当数据被传输时,下载器应该将多个块放在队列中一并请求,以获取更好的TCP性能(这叫做流水线)。另一方面,不能立即写入TCP缓冲器的请求应当在存储器中排队,而不是保留在应用级网络缓冲器中,因此当发生阻塞时,它们都可以被抛弃。

对等线协议由握手和包含长度前缀消息的不间断流组成。握手的开头是字符19(十进位的),然后是字符串BitTorrent protocol。前导字符是一个长度前缀,放在那里,希望其他新协议也这样,可以彼此区分。

在协议中发送的所有后面出现的整数被编码为四字节(大端排序)。
在固定的报头之后有八个保留字节,在所有当前实现中它们都为零。如果您希望使用这些字节扩展协议,请与Bram Cohen协调,以确保所有扩展都兼容。

接下来是来自metainfo文件的info值的bencoded形式的20字节sha1散列。(这个和发送给tracker的info_hash是一样的值,只在这里它是原始的,而不是引用)。如果双方都不发送相同的值,它们将断开连接。一个可能出现的情况是如果下载者想通过单个端口进行多次下载,他们会等待传入的连接,并首先给出一个下载哈希,并且如果它在它们的列表中,则用同一个哈希进行响应。

在下载哈希值之后,20个字节的对等体ID在跟踪器请求中报告并且包含在跟踪器响应中的对等体列表中。如果接收方的对等体ID与发起方期望的不匹配,则它切断连接。

这是握手,接下来是一个交替的长度前缀和消息的流。长度为零的消息是保持活动,并被忽略。 Keepalive通常每两分钟发送一次,但请注意,当预期数据时,超时可以更快地完成。