BitTorrent 的 UDP Tracker 协议

简介

为了在一个 BitTorrent 下载群体中找到其他的节点,一个客户端会向一个追踪器发出宣告自己的请求。这个请求使用 HTTP 协议,并包含一些参数,比如 info_hashkeypeer_idportdownloadedleftuploadedcompact 等。追踪器会返回一组节点(主机和端口)以及其他一些信息给客户端。这个请求和响应都非常简短。由于使用的是 TCP 协议,需要在发送请求前打开连接,在完成请求后关闭连接,这会引入额外的开销。

系统开销

使用 HTTP 协议会带来很大的系统开销,因为在以太网层、IP层、TCP层和HTTP层都会有开销,并且一个请求加上响应包含 50 个节点需要使用大约 10 个数据包,总共使用的字节数约为 1206。通过使用基于 UDP 的协议可以显著减少这种开销,本文提出的协议只需 4 个数据包和约 618 个字节,将流量减少了 50%。虽然对于客户端来说每小时节省 1KB 并不重要,但对于为百万节点提供服务的 Tracker 来说,减少流量的 50% 非常重要。此外,基于 UDP 的二进制协议不需要复杂的解析器和连接处理,降低了 Tracker 代码的复杂性并提高了其性能。

UDP 连接 / 欺骗攻击

在理想情况下,只需要两个数据包就可以完成通信。在使用 UDP 协议时,由于 UDP 协议是无连接的,因此可能会存在伪造源地址的情况。因此,tracker 必须采取措施来确保不会出现欺骗行为。为了避免这种情况发生,Tracker(一个网络中用来协调客户端与其他peer之间数据共享的服务器程序)采取一些措施。

当客户端向 tracker 发送一个请求时,tracker 会生成一个随机数(connection_id),并将其发送给客户端。客户端再次向 tracker 发送请求时,需要带上这个 connection_id,以便 tracker 可以验证请求的来源是否合法。如果客户端伪造了源地址,则不可能收到 tracker 发回的 connection_id,从而请求会被 tracker 拒绝。

为了确保 connection_id 不被客户端猜测到,tracker 可以采用类似于 TCP 握手和 syn-cookie 的方法,在服务器端存储 connection_id,并仅在特定情况下才返回给客户端使用。一个 connection_id 可以用于多个请求,并且客户端可以在接收到 connection_id 后的一分钟内使用它。tracker 应该在发出 connection_id 后的两分钟内接受它,并验证来自客户端的请求是否匹配这个 connection_id。

超时

UDP 是一种不保证数据包可靠传输的协议,这意味着如果在传输过程中丢失了数据包,UDP 不会自动重传。相反,使用 UDP 的应用程序需要负责处理丢失的数据包,并在必要时进行重传。

为了处理丢失的数据包,客户端应该在发送请求后等待服务器的响应。如果在 15 * 2 ^ n 秒后没有收到响应,则客户端应该重新发送请求。重新发送请求的时间间隔从 15 秒开始,每次尝试都会加倍,最多尝试 8 次,即 3840 秒。

需要注意的是,如果连接ID已经过期,在重新发送请求之前需要重新请求新的连接 ID。这确保了请求将被正确地认证并由服务器进行处理。

样例

常规广播:

t = 0: connect request
t = 1: connect response
t = 2: announce request
t = 3: announce response

连接超时:

t = 0: connect request
t = 15: connect request
t = 45: connect request
t = 105: connect request
etc

广播超时:

t = 0:
t = 0: connect request
t = 1: connect response
t = 2: announce request
t = 17: announce request
t = 47: announce request
t = 107: connect request (because connection ID expired)
t = 227: connect request
etc

多重请求:

t = 0: connect request
t = 1: connect response
t = 2: announce request
t = 3: announce response
t = 4: announce request
t = 5: announce response
t = 60: announce request
t = 61: announce response
t = 62: connect request
t = 63: connect response
t = 64: announce request
t = 64: scrape request
t = 64: scrape request
t = 64: announce request
t = 65: announce response
t = 66: announce response
t = 67: scrape response
t = 68: scrape response

UDP Tracker 协议

所有数值(例如整数或浮点数)在发送时都应该按照网络字节顺序(大端序)进行编码。此外,不应该预期每个数据包都具有确定的大小。因为将来可能会通过添加新功能来增加数据包的大小。

连接

Before announcing or scraping, you have to obtain a connection ID.

  1. Choose a random transaction ID.
  2. Fill the connect request structure.
  3. Send the packet.

对于进行”announcing” 或 “scraping” 操作之前,需要先获取一个连接ID的过程。步骤如下:

  1. 随机生成一个 Transaction ID 作为唯一标识符。
  2. 填充连接请求结构体。
  3. 发送请求数据包。

连接请求:

Offset  Size            Name            Value
0       64-bit integer  protocol_id     0x41727101980 // magic constant
8       32-bit integer  action          0 // connect
12      32-bit integer  transaction_id
16
  1. 接收数据包。
  2. 检查它的长度是否至少为16字节。
  3. 检查接收到的数据包中的 Transaction ID 是否与之前发送请求时选择的 ID 一致。
  4. 检查数据包中的操作是否为”connect”。
  5. 将数据包中的 Connection ID 存储到本地,并用于后续操作。

连接响应:

Offset  Size            Name            Value
0       32-bit integer  action          0 // connect
4       32-bit integer  transaction_id
8       64-bit integer  connection_id
16

广播

  1. 从系统中随机选择一个 Transaction ID。
  2. 填入一个广播请求结构。
  3. 发送数据包。

IPv4 广播请求:

Offset  Size    Name    Value
0       64-bit integer  connection_id
8       32-bit integer  action          1 // announce
12      32-bit integer  transaction_id
16      20-byte string  info_hash
36      20-byte string  peer_id
56      64-bit integer  downloaded
64      64-bit integer  left
72      64-bit integer  uploaded
80      32-bit integer  event           0 // 0: none; 1: completed; 2: started; 3: stopped
84      32-bit integer  IP address      0 // default
88      32-bit integer  key
92      32-bit integer  num_want        -1 // default
96      16-bit integer  port
98
  1. 接收数据包。
  2. 检查数据包的长度是否至少为 20 字节。
  3. 检查数据包中的交易 ID 是否与之前选择的交易 ID 相等,以确保接收到的是之前发送的通告请求的响应。
  4. 检查数据包中的操作是否为“announce”。
  5. 进行时间间隔检查。如果与上次广播请求时间不足一定时间间隔(interval秒),则不再进行广播请求;否则,可以继续进行广播请求或等待事件触发后再次请求。

大多数 Tracker 只在某些特定情况下才会考虑 IP 地址字段。

IPv4 广播请求:

Offset      Size            Name            Value
0           32-bit integer  action          1 // announce
4           32-bit integer  transaction_id
8           32-bit integer  interval
12          32-bit integer  leechers
16          32-bit integer  seeders
20 + 6 * n  32-bit integer  IP address
24 + 6 * n  16-bit integer  TCP port
20 + 6 * N

IPv6

IPv6 和 IPv4 在协议结构上的区别以及如何在使用中进行适配。其中,IPv6 和 IPv4 的消息格式基本相同,但是在回应消息中,<IP地址、TCP端口>对的步进大小从 6 字节变成了 18 字节。此外,在请求消息中,IP 地址字段仍然是 32 位宽度,不能用于 IPv6,并且始终应该设置为 0。

同时,该段落还提到了适配的方式:根据 UDP 包的地址族来确定所使用的格式,即来自 IPv4 地址的数据包使用 IPv4 格式,来自 IPv6 地址的数据包使用 IPv6 格式。最后,对于将主机名解析为 IPv4 和 IPv6 并向两个传输使用相同密钥的客户端,需要确保跟踪器能够准确地匹配两个通告,以实现准确统计。

抓取

最多可以同时获取约 74 个种子文件的信息。无法使用此协议完成完整的数据抓取。

  1. 随机选择一个 Transaction ID。
  2. 填充抓取请求结构。
  3. 发送数据包。

抓取请求:

Offset          Size            Name            Value
0               64-bit integer  connection_id
8               32-bit integer  action          2 // scrape
12              32-bit integer  transaction_id
16 + 20 * n     20-byte string  info_hash
16 + 20 * N
  1. 接受数据包。
  2. 检查数据包的长度是否至少为 8 字节。
  3. 检查该数据包中的 Transaction ID 是否与之前你选择的相同。
  4. 检查数据包中的操作是否为“scrape”。

抓取响应:

Offset      Size            Name            Value
0           32-bit integer  action          2 // scrape
4           32-bit integer  transaction_id
8 + 12 * n  32-bit integer  seeders
12 + 12 * n 32-bit integer  completed
16 + 12 * n 32-bit integer  leechers
8 + 12 * N

如果 tracker 遇到错误,可能会发送一个错误数据包。

  1. 接受数据包。
  2. 检查数据包的长度是否至少为 8 字节。
  3. 检查该数据包中的 Transaction ID 是否与之前你选择的相同。

错误

错误响应:

Offset  Size            Name            Value
0       32-bit integer  action          3 // error
4       32-bit integer  transaction_id
8       string  message

现有实例

IMFile,Azureus,libtorrent,opentracker,XBT Client 和 XBT Tracker 支持该协议。

插件

为了保持协议的兼容性,一般不会在协议中包含扩展位或版本字段。客户端和 Tracker 也不应该假定数据包的大小。这样做可以在不破坏兼容性的情况下添加额外的字段。换句话说,通过避免固定数据包长度和格式,协议可以更容易地进行扩展和更新,并且可以保持向后兼容性。

总结

UDP Tracker Protocol for BitTorrent 是一种用于点对点文件共享协议BitTorrent中的追踪器通信协议。它是基于UDP协议的,相较于HTTP协议,可以更快地传输数据,同时也具有更好的扩展性和高效性。

通过UDP Tracker Protocol,BitTorrent客户端可以向追踪器发送请求,获取连接到种子的其他用户列表。这些用户可以帮助下载者提供文件块,提高下载速度。此外,追踪器还可以提供有关特定种子的统计信息,如上传和下载速度、剩余时间等。

尽管UDP Tracker Protocol具有许多优点,但由于其本质上是无状态的,因此在某些情况下可能会遇到一些问题,例如有时会丢失请求或响应。为了解决这些问题,很多BitTorrent客户端还支持HTTP Tracker Protocol,同时使用两种不同的追踪器通信协议以提高可靠性。

参考链接

  • http://www.bittorrent.org/beps/bep_0015.html
  • http://xbtt.sourceforge.net/udp_tracker_protocol.html
  • http://www.rasterbar.com/products/libtorrent/udp_tracker_protocol.html
  • http://opentracker.blog.h3q.com/
  • http://bittorrent.org/beps/bep_0041.html

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注