BitTorrent 中的 Extension Protocol 是一种用于扩展和增强 BitTorrent 下载协议功能的机制。它允许客户端通过发送和响应与标准协议不同的消息来进行通信。这些消息可以实现新的功能,如 DHT(分布式哈希表)交换、PEX(对等交换)以及支持更高级别的加密。
Extension Protocol 在 BitTorrent 协议中起着至关重要的作用,因为它允许客户端进行更有效和更可靠的数据交换,并提供了更好的隐私和安全性。例如,DHT 支持无需 Tracker 即可查找其他已连接到网络的对等方;而 PEX 则允许客户端直接与其他客户端通信,从而使数据交换更加稳定和可靠。
协议简介
这个协议的目的是为 BitTorrent 协议的扩展提供一个简单而轻量的传输方式。支持这个协议可以很容易地添加新的扩展功能,而不会干扰标准的 BitTorrent 协议或不支持你想要添加的扩展功能的客户端。换句话说,这个协议提供了一种灵活、可扩展的机制,使得开发者能够在不影响其他用户的情况下,对 BitTorrent 协议进行个性化的扩展和定制。
在 BitTorrent 协议中,为了向其他支持的客户端传播自己,会使用保留字节中的一个字节。其中,用于扩展协议的字节是从右边开始数的第 20 个(从0开始计数)。因此,可以通过表达式(reserved_byte[5] & 0x10)来检查客户端是否支持扩展消息传输。这个表达式的含义是,将保留字节中的第 5 个字节与 0x10 进行按位与运算,如果结果不为0,则表示客户端支持扩展消息。
建立对协议的支持后,客户端应该支持 1 条新消息:
name | id |
---|---|
extended | 20 |
这个消息与其他BitTorrent 消息一样,以 4 字节的长度前缀和一个单字节的标识符(在本例中为20)发送。在消息有效负载的开头,有一个单字节的消息标识符。该标识符可以引用不同的扩展消息,但只指定了一个 ID,即 0。如果 ID 是 0,则表示这个消息是一个握手消息,下面会进行描述。一般扩展消息的布局如下所示(包括 BitTorrent 协议使用的消息头):
size | description |
---|---|
uint32_t | 长度前缀。指定整个消息的字节数。(大端序 |
uint8_t | bittorrent 消息 ID, = 20 |
uint8_t | 扩展消息 ID。0 = 握手,>0 = 握手指定的扩展消息。 |
握手消息
握手消息的有效负载是一个 bencoded 字典。字典中的所有项都是可选的。客户端应该忽略字典中未知的名称。字典中的所有部分都区分大小写。字典中定义了以下项目:
name | description |
---|---|
m | 支持的扩展消息字典将每个扩展消息的名称映射到一个扩展消息 ID。对这些 ID 唯一的要求是,没有两个扩展消息可以共享同一个 ID。将扩展号设置为零意味着该扩展未被支持/禁用。客户端应忽略其不识别的任何扩展名。 扩展消息ID是用于将扩展消息发送到发送此握手的同行方的 ID。也就是说,这些 ID 是特定于此特定对等体的本地 ID。 |
这里列出了一些实现可能选择支持的项目:
name | description |
---|---|
p | 本地 TCP 监听端口。允许每一方了解另一方的 TCP 端口号。请注意,接收连接的一方不需要发送此扩展消息,因为其端口号已知。 |
v | 客户端名称和版本(作为 UTF-8 字符串)。这是比依赖对等方 ID 编码更可靠的识别客户端的方法。 |
yourip | 一个包含 IP 地址紧凑表示的字符串,用于表示此节点看到您的 IP 地址。也就是说,这是接收方的外部 IP 地址(不包括端口)。它可以是 IPv4(4个字节)或 IPv6(16个字节)地址。 |
ipv6 | 如果此节点具有 IPv6 接口,则这是该地址的紧凑表示(16个字节)。客户端可能更喜欢通过 IPv6 地址进行连接。 |
ipv4 | 如果此节点具有 IPv4 接口,则这是该地址的紧凑表示(4 个字节)。客户端可能更喜欢通过 IPv4 地址进行连接。 |
reqq | 一个整数,表示此客户端支持的未完成请求消息数量,而不会丢失任何消息。在 libtorrent 中,默认值为 250。 |
握手字典还可以包括扩展的握手信息,例如支持加密头部或任何可能的内容。
以下是握手消息负载的示例内容:
Dictionary | |
---|---|
m | Dictionary LT_metadata······1 ut_pex··················2 |
p | 6881 |
v | “µTorrent 1.2” |
并以编码形式:
d1:md11:LT_metadatai1e6:µT_PEXi2ee1:pi6881e1:v13:\xc2\xb5Torrent 1.2e
为了避免扩展名不小心发生冲突,应该在扩展名前加上一个或两个字符的代码,以标识引入扩展的客户端。这适用于扩展消息的名称以及放在顶层字典中的任何其他信息。除非由本规范定义,否则所有一字节和两字节标识符均无效。
此消息应在标准 bittorrent 握手后立即发送到支持此扩展协议的任何节点。在连接的整个周期内,可以多次发送握手消息,发送方不应断开连接。实现可以选择忽略后续的握手消息(或其部分)。
随后的握手消息可用于在不重新启动连接的情况下启用/禁用扩展。如果节点持在运行时更改扩展,则应注意 m 字典是累加的。它包含扩展列表的实际更改就足够了。要在运行时禁用对 LT_metadata 的支持,而不影响任何其他扩展,应发送此消息:d11:LT_metadatai0ee。如上所述,值 0 用于关闭扩展。
必须为每个节点存储扩展 ID,因为每个节点可能具有相同扩展的不同 ID。
该规范故意没有指定任何扩展,例如节点交换或元数据交换。该协议仅是 bittorrent 协议实际扩展的传输方式,上面的示例中命名的扩展(例如 p)只是可能扩展的示例。
合理的依据
为了避免使用全局消息 ID 注册表,扩展消息的 ID 在握手过程中被定义的原因是为了确保扩展消息名称具有唯一性,这样做比使用全局注册表更容易。惯例是在扩展消息名称上使用两个字母前缀,该前缀将标识最先实现该扩展消息的客户端。例如,LT_metadata 由 libtorrent 实现,因此它具有 LT 前缀。
如果支持扩展的客户端可以决定其接收到的消息将具有哪些数字,那么这些数字就是该客户端内的常量。也就是说,它们可以用于 switch 语句。对于另一端来说,存储一个包含每个消息期望的 ID 的数组并在每次发送扩展消息时使用该数组进行查找非常容易。
使用字典而不是使用(用于隐式分配索引号扩展的)数组的原因是,如果客户端想要禁用某些扩展,ID 号码将会改变,它将无法使用常量(因此,不能在 switch 中使用它们)。如果消息 ID 直接映射到 BitTorrent 消息 ID,则还可以将握手中的扩展映射到具有固定消息 ID 的现有扩展。
使用单个字节作为扩展消息标识符的原因是为了遵循 BitTorrent 规范的单个字节消息标识符。这被认为足够了。它不会限制扩展的总数,只会限制同时使用的扩展数。
使用单个字节标识符作为标准化握手标识符的原因是:1)主线 DHT 使用单个字节标识符;2)节省带宽。更长消息的唯一优点是使协议对人类更易读,但是 BT 协议并不是设计成人类可读的协议,所以为什么要费心呢。
总的来说,Extension Protocol 是 BitTorrent 协议的一个重要组成部分,它为用户提供了更多的功能和更好的使用体验。
参考链接
- http://www.bittorrent.org/beps/bep_0010.html