BitTorrent 原理介绍

原理简述

BitTorrent(简称BT)协议其实是一个协议簇,BEP-3 是其基本协议内容,其他的大部分都是围绕这个来进行扩展或补充。
要想从BT网络中下载一个资源,必须具备以下部分:

  • 种子文件
  • BT客户端(专门解析BT协议的程序,比如迅雷,电驴)
  • Tracker服务器

下载资源的时候,客户端首先解析种子文件,得到Tracker服务器的地址和资源信息,通过和Tracker服务器沟通得到其他已经下载该资源的peers信息,然后再和这些peers沟通得到自己想要的部分,即互通有无。由于把文件分成很多块来同时从不同的地方下载,这也就是为什么BT通常下载快的原因。

相关概念

Tracker

收集下载者信息的服务器,并将此信息提供给其他下载者,使下载者们相互连接起来,传输数据。

peers

其他已经拥有该资源的客户端或者发布该资源的人

种子

后缀是 .torrent,本质上是一个由bencode编码的文本文件,其把资源分成很多虚拟块,并记录每个块的hash值,另外上面还记录着其他信息,比如文件大小、名字、Tracker服务器等

bencode

bencode是BT协议中的编码方式.这种编码方式支援四种资料型态:字元串,整数,串列,字典表。Bencode最常被用在.torrent档中,档案里的元数据都是Bencode过的字典表。其也被用在tracker返回响应时使用。

DHT 网络

通过上面我们知道,Tracker服务器在资源下载的过程中起着至关重要的作用,只有通过它我们才能得到其他peers的信息,才能够下载,但这同时也成了BT协议的一个弱点,如果Tracker服务器挂掉了或者被封被屏蔽,整个网络也就瘫痪了。
后来聪明的人类发明了另外一种协议,就是 Distributed hash table, 简称DHT,这个协议就是用来弥补这个弱点的。

DHT全称 Distributed Hash Table,中文翻译过来就是分布式哈希表。它是一种去中心化的分布式系统,特点主要有自动去中心化,强大的容错能力,支持扩展。另外它规定了自己的架构,包括keyspace和overlay network(覆盖网络)两部分。但是他没有规定具体的算法细节,所以出现了很多不同的实现方式,比如Chord,Pastry,Kademlia等。BitTorrent中的DHT是基于Kademlia的一种变形,它的官方名称叫做 Mainline DHT。

DHT人如其名,把它看成一个整体,从远处看它,它就是一张哈希表,只不过这张表是分布式的,存在于很多机器上。它同时支持set(key, val),get(key)操作。DHT可以用于很多方面,比如分布式文件系统,即时消息(IM),以及我们最熟悉的点对点文件共享(比如BT协议)等。

BT协议簇中的DHT协议 是基于 Kademlia协议 建立的,其基本思想很好理解。DHT 由很多节点组成,每个节点保存一张表,表里边记录着自己的好友节点。当你向一个节点A查询另外一个节点B的信息的时候,A就会查询自己的好友表,如果里边包含B,那么A就返回B的信息,否则A就返回距离B距离最近的k个节点。然后你再向这k个节点再次查询B的信息,这样循环一直到查询到B的信息,查询到B的信息后你应该向之前所有查询过的节点发个通知,告诉他们,你有B的信息。

下载原理

获取tracker 或DHT nodes信息

根据 BitTorrent 协议,文件发布者会根据要发布的文件生成提供一个 .torrent 文件。
客户端可从 Web 服务器上下载种子文件,并从中得到 Tracker 服务器 URL 和 DHT 网络 nodes 等信息。

获取Peers信息

根据 Tracker URL 与 Tracker 服务器建立连接,并从服务器上得到 Peers 信息。
或者根据 nodes 与 DHT 网络中节点通信,并从节点上得到 Peers 信息。

获取分块文件

根据 Peers 信息与一个 Peer 建立连接,依据 Peer wire 协议完成握手,
并从 Peer 端下载数据文件。同时监听 Peer 的连接,并给 Peer 上传数据文件。

依据得到 Peers 信息的途径的不同,可分为使用 Tracker 服务器和使用 Trackerless DHT 网络两种方式。

基于 HTTP 的 Tracker 协议,
基于 UDP 的 Trackerless 协议,
基于 TCP 的 Peer wire 协议。

实践分析

为了进一步了解bt协议,我从实践角度学习下别的怎么实现协议的。
在这里看的是Ttorrent, a Java implementation of the BitTorrent protocol 的源代码。

分析种子文件,输出种子信息和tracker地址

种子文件是从torrage.info上下载下来的,样本

新建java工程,在pom文件中,导入ttorrent-core和日志库(很关键的,后面输出种子中的信息要靠它)

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>com.turn</groupId>
<artifactId>ttorrent-core</artifactId>
<version>1.5</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.7</version>
</dependency>

示例代码
主要是读取种子信息,输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static void printTorrentInfo() throws Exception {
Torrent t = SharedTorrent.load(new File("640FE84C613C17F663551D218689A64E8AEBEABE.torrent"));
System.out.println("Name: " + t.getName());
System.out.println("Size: " + t.getSize());
System.out.println("Comment: " + t.getComment());
System.out.println("TracertCount: " + t.getTrackerCount());
System.out.println("Fileanmes: " + t.getFilenames());

System.out.println("AnnounceList:");
List<List<URI>> list = t.getAnnounceList();
for (List<URI> list2 : list) {
for (URI uri : list2) {
System.out.println(uri);
}
}
}

public static void main(String[] args) throws Exception {
printTorrentInfo();
}

可能的输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
INFO com.turn.ttorrent.common.Torrent - Multi-file torrent information:
INFO com.turn.ttorrent.common.Torrent - Torrent name: slackware-12.2-iso
INFO com.turn.ttorrent.common.Torrent - Announced at:
INFO com.turn.ttorrent.common.Torrent - 1. udp://exodus.desync.com:6969
INFO com.turn.ttorrent.common.Torrent - 2. udp://tracker.leechers-paradise.org:6969/announce
INFO com.turn.ttorrent.common.Torrent - 3. udp://open.demonii.com:1337/announce
INFO com.turn.ttorrent.common.Torrent - 4. udp://tracker.istole.it:80/announce
INFO com.turn.ttorrent.common.Torrent - 5. http://tracker.istole.it/announce
INFO com.turn.ttorrent.common.Torrent - 6. udp://tracker.publicbt.com:80/announce
INFO com.turn.ttorrent.common.Torrent - 7. http://tracker.publicbt.com/announce
INFO com.turn.ttorrent.common.Torrent - 8. udp://tracker.ccc.de:80/announce
INFO com.turn.ttorrent.common.Torrent - 9. http://tracker.ccc.de/announce
INFO com.turn.ttorrent.common.Torrent - 10. http://pow7.com/announce
INFO com.turn.ttorrent.common.Torrent - 11. http://tracker1.transamrit.net:8082/announce
INFO com.turn.ttorrent.common.Torrent - 12. http://tracker2.transamrit.net:8082/announce
INFO com.turn.ttorrent.common.Torrent - 13. http://tracker3.transamrit.net:8082/announce
INFO com.turn.ttorrent.common.Torrent - 14. http://tracker.ilibr.org:6969/announce
INFO com.turn.ttorrent.common.Torrent - Created on..: Tue Dec 09 21:16:10 CST 2008
INFO com.turn.ttorrent.common.Torrent - Comment.....: Torrent downloaded from torrent cache at http://torrage.info
INFO com.turn.ttorrent.common.Torrent - Found 3 file(s) in multi-file torrent structure.
DEBUG com.turn.ttorrent.common.Torrent - 1. slackware-12.2-iso\slackware-12.2-install-dvd.iso (4,154,949,632 byte(s))
DEBUG com.turn.ttorrent.common.Torrent - 2. slackware-12.2-iso\slackware-12.2-install-dvd.iso.asc (197 byte(s))
DEBUG com.turn.ttorrent.common.Torrent - 3. slackware-12.2-iso\slackware-12.2-install-dvd.iso.md5 (65 byte(s))
INFO com.turn.ttorrent.common.Torrent - Pieces......: 1982 piece(s) (2097152 byte(s)/piece)
INFO com.turn.ttorrent.common.Torrent - Total size..: 4,154,949,894 byte(s)
...

解析的关键代码在com.turn.ttorrent.common.Torrentpublic Torrent(byte[] torrent, boolean seeder)的方法内。

可以看到,slackware被分成1982份,总共4G多。

分析从tracker上获取peers的过程

很可惜,自己的网络好像一个peers都看不到。
放到服务器上,倒是可以看到几个peers

本机上日志这样的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[main] INFO com.turn.ttorrent.client.announce.Announce - Initialized announce sub-system with 14 trackers on slackware-12.2-iso.
[main] INFO com.turn.ttorrent.client.Client - BitTorrent client [..386134] for slackware-12.2-iso started and listening at 192.168.3.1:49156...
[bt-client(..386134)] INFO com.turn.ttorrent.client.SharedTorrent - Analyzing local data for slackware-12.2-iso with 4 threads (1982 pieces)...
[bt-client(..386134)] INFO com.turn.ttorrent.client.SharedTorrent - ... 10% complete
[bt-client(..386134)] INFO com.turn.ttorrent.client.SharedTorrent - ... 20% complete
...
[bt-client(..386134)] INFO com.turn.ttorrent.client.SharedTorrent - ... 80% complete
[bt-client(..386134)] INFO com.turn.ttorrent.client.SharedTorrent - ... 90% complete
[bt-client(..386134)] DEBUG com.turn.ttorrent.client.SharedTorrent - slackware-12.2-iso: we have 0/4154949894 bytes (0.0%) [0/1982 pieces].
[bt-announce(..386134)] INFO com.turn.ttorrent.client.announce.Announce - Starting announce loop...
[bt-announce(..386134)] INFO com.turn.ttorrent.client.announce.UDPTrackerClient - Announcing STARTED to tracker with 0U/0D/4154949894L bytes...
[bt-client(..386134)] INFO com.turn.ttorrent.client.Client - SHARING 0/1982 pieces (0.00%) [0/0] with 0/0 peers at 0.00/0.00 kB/s.
[bt-client(..386134)] INFO com.turn.ttorrent.client.Client - SHARING 0/1982 pieces (0.00%) [0/0] with 0/0 peers at 0.00/0.00 kB/s.
[bt-client(..386134)] INFO com.turn.ttorrent.client.Client - SHARING 0/1982 pieces (0.00%) [0/0] with 0/0 peers at 0.00/0.00 kB/s.

服务器上日志

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[main] INFO com.turn.ttorrent.client.Client - BitTorrent client [..333338] for slackware-12.2-iso started and listening at 10.140.0.2:49152...
[bt-client(..333338)] INFO com.turn.ttorrent.client.SharedTorrent - Analyzing local data for slackware-12.2-iso with 1 threads (1982 pieces)...
[bt-client(..333338)] INFO com.turn.ttorrent.client.SharedTorrent - ... 10% complete
[bt-client(..333338)] INFO com.turn.ttorrent.client.SharedTorrent - ... 20% complete
...
[bt-client(..333338)] INFO com.turn.ttorrent.client.SharedTorrent - ... 80% complete
[bt-client(..333338)] INFO com.turn.ttorrent.client.SharedTorrent - ... 90% complete
[bt-client(..333338)] DEBUG com.turn.ttorrent.client.SharedTorrent - slackware-12.2-iso: we have 0/4154949894 bytes (0.0%) [0/1982 pieces].
[bt-announce(..333338)] INFO com.turn.ttorrent.client.announce.Announce - Starting announce loop...
[bt-announce(..333338)] INFO com.turn.ttorrent.client.announce.UDPTrackerClient - Announcing STARTED to tracker with 0U/0D/4154949894L bytes...
[bt-client(..333338)] INFO com.turn.ttorrent.client.Client - SHARING 0/1982 pieces (0.00%) [0/0] with 0/0 peers at 0.00/0.00 kB/s.
[bt-announce(..333338)] WARN com.turn.ttorrent.client.announce.Announce - Error while announcing STARTED to tracker: Unresolved address
[bt-announce(..333338)] DEBUG com.turn.ttorrent.client.announce.Announce - Switched to tracker client for udp://tracker.leechers-paradise.org:6969/announce (tier 1, position 0).
[bt-client(..333338)] INFO com.turn.ttorrent.client.Client - SHARING 0/1982 pieces (0.00%) [0/0] with 0/0 peers at 0.00/0.00 kB/s.
[bt-announce(..333338)] INFO com.turn.ttorrent.client.announce.UDPTrackerClient - Announcing STARTED to tracker with 0U/0D/4154949894L bytes...
[bt-announce(..333338)] INFO com.turn.ttorrent.client.announce.Announce - Setting announce interval to 1682s per tracker request.
[bt-announce(..333338)] INFO com.turn.ttorrent.client.Client - Got 18 peer(s) in tracker response.
[bt-connect-1] INFO com.turn.ttorrent.client.ConnectionHandler - Connecting to peer://104.199.220.35:49152/? [Ci|Ci|0]...
[bt-connect-2] INFO com.turn.ttorrent.client.ConnectionHandler - Connecting to peer://176.31.234.48:50007/? [Ci|Ci|0]...
[bt-connect-3] INFO com.turn.ttorrent.client.ConnectionHandler - Connecting to peer://188.12.47.215:22891/? [Ci|Ci|0]...
[bt-connect-4] INFO com.turn.ttorrent.client.ConnectionHandler - Connecting to peer://220.147.5.16:24874/? [Ci|Ci|0]...

明显本地上没有找到peers,服务器上找到了。
其中的原因,还得进一步分析。
时间有限,这里只做个示例。

参考