前言

经常在网上飙车的老司机应该都知道BT下载,但是有时候拿到了种子却下载不动,会不会很抓狂,是不是还觉得是自己网不行,那作为一个合格的老司机,我们需要探究一下下载不动的原因是什么,BT 的运作方式是怎样的,如果你也有这样的疑惑,那么,系好安全带,我们一起来了解一下什么是 BT。

从 P2P 说起

P2P 有着很广泛的应用,比如P2P金融(雾),区块链,BT下载等。它的关键字是去中心化,依靠用户群(Peers)来互相传输数据,符合这种特征的都可以称之为P2P。

BitTorrent(简称BT)由 Bram Cohen 于2001年提出,其核心思想是通过去中心化的用户协作实现大文件高效分发。与传统HTTP依赖单一服务器不同,BT将文件传输负担分散到参与下载的所有用户节点(Peer)上,实现“下载即上传,人人为我,我为人人”的共享模式。这一设计显著降低了服务器带宽压力,并在用户规模增大时形成正反馈效应——参与者越多,整体下载速度越快。

种子文件

大家肯定有在互联网上下载各种资源的经历,比如电影电视剧,我们在网上一搜,就会搜到一些不知名的小网站,网站上通常会提供一个叫做「种子」的东西,我们使用时只需要把种子下载到电脑上,通常是一个后缀为.torrent的文件, 然后用迅雷或者其他的下载工具下载。

种子文件是采用 Bencode 编码的元数据文件,Bencode 编码规范如下

数据类型 编码规则 示例
字符串 长度:内容 “hello” → “5:hello”
整数 i数字e 123 → “i123e”
列表 l元素e [1,”a”] → “li1e1:ae”
字典 d排序键值对e {“b”:2,”a”:1} → “d1:ai1e1:bi2ee”

种子文件核心字段包括

1
2
3
4
5
6
7
8
9
10
11
{
"announce": "https://tracker.example.com", # Tracker主地址
"announce-list": [["备用Tracker"]], # 备用Tracker
"info": {
"name": "文件名",
"piece length": 262144, # 分块大小(通常256KB)
"pieces": ["sha1哈希值列表"], # 分块校验码
"length": 文件总字节数,
"files": [{"path":["目录结构"],"length":大小}] # 多文件结构
}
}

一个种子文件中的 info.pieces 字段包含了每个小块的哈希值,而每个块的大小都是 256KB。这样就相当于把下载任务分解了,分解成了下载若干个大小为 256KB 的小块的任务。采用并发的方式去依次请求每个块的数据,然后计算好数据的位置放进最终结果中返回,然后把内存中的数据写入硬盘。

BitTorrent 工作全流程

简单讲下BT下载的整个流程

  1. 种子发布者制作种子,且向 Tracker 服务器表明,大家要下载这个种子就来找我。(Tracker 的地址就是种子文件中 announce 字段中的 url);
  2. 种子发布者把做好的种子分享到互联网;
  3. 下载者在互联网上获取到种子文件;
  4. 下载者本地的 BT 客户端解析种子文件,拿到 Tarcker 地址,向 Tarcker 发起请求(HTTP或UDP),获取其他 Peer 的地址;
  5. Tracker接收到请求后,去自己的存储里找拥有这个种子中的文件的 peers 的 IP:port,返回给下载者,并且把当前下载者的 IP:Port 加入服务器的存储;
  6. 下载者与其他 Peer 建立连接,由于一个文件被分成了若干个文件块,所以下载者可以和多个 Peer 下载不同的块,下载完成后,校验块的哈希值,保存在本地。(这也是下载种子的人越多,下载速度越快的原因);
  7. 整个文件下载完成时,校验整个文件哈希值,不出意外,下载成功;
  8. BT客户端不要关闭,自己作为 Peer 服务 BT 网络中的其他人;

Tracker 的作用

种子文件中的 announce 字段中包含了一个 url ,这个 url 也就是 tracker 服务器。

服务器的作用是作为 peers 沟通的桥梁而存在,当下载者要下载某一个资源的时候,就会去向服务器询问,服务器查询之后如果发现自己保存了这个资源的其他节点,就把这些节点的地址返回,然后客户端知道这些 IP:Port 后,就去与其他 Peer 建立连接。

Tracker 不存储任何具体资源的文件信息,只存储文件的哈希值,来帮助 Peers 来建立连接。

发布者做种

种子只有先被制作发布,才能使用。一般来说,使用 BT 下载软件进行做种的时候,下载软件会内置几个 tracker 服务器,当然也可以自己找一些 tarcker 的地址添加进去,BT客户端会向这些 tracker 发起请求。Tracker 服务器就会记录下来上传者的 IP:Port ,以便于传输给后续下载者进行下载。

那如果下载者要下载的时候,所有拥有这些文件的人都不在线怎么办。那就真没办法了,这种种子也叫「死种」,因为没人上传,这也就是网上很多种子下载不动的原因。

下载者下载

下载者与 Trakcer 的通信方式有 UDP 和 HTTP 两种协议,具体使用哪一种,看种子里面包含的信息是 udp 还是 http。下面我们以 HTTP 的方式来进行探究:

下载者向 Tracker 发起一个 GET 请求,请求的格式包含的关键字段:

Key 含义
info_hash 文件的哈希值,是一个资源的唯一标志
peer_id 由本地客户端自己生成,一个随机20字节的字符串,向网络中的其他Peers标记自己的身份
Port 本地客户端的监听接口,用于接收其他Peer发来的消息

Tracker 接收到 GET 请求后,Tracker服务器就会反连(NatCheck)下载者的IP地址和端口。然后服务器返回现在正在下载这个文件的所有公网用户的IP地址和端口列表,返回给BT客户端。最后如果该用户是公网用户,Tracker服务器会把用户提交的IP地址和端口保存下来,这样其他人就可以找到该用户。

BT客户端得到这些其他用户IP后,就可以直接连接到这些IP和端口下载资料了。BT客户端会到所有的用户去寻找自己要下载的东西。BT客户端每找到一个用户就建立一个 Socket来下载 ,所以下载的人越多,速度就越快。

Peer通信协议

节点间通过 TCP(默认端口6881-6889)或 UTP(基于UDP的拥塞控制协议)传输数据,消息结构如下:

消息类型 消息ID 字段说明
握手(Handshake) - 协议名(19字节)、InfoHash、Peer ID
Bitfield 5 比特位图(标识已拥有的分块)
Request 6 请求的分块索引、偏移量、长度
Piece 7 分块索引、偏移量、实际数据
Have 4 声明新获得的分块索引
Choke/Unchoke 0/1 阻塞/解阻塞对方下载权限(流量控制)

校验哈希值

当下载者收到数据提供者发送过来的二进制数据后,会计算一下这个二进制数据的哈希值,然后和自己的 torrent 文件中的对应块的哈希值进行比较,如果哈希值一样,就代表这块块有效,就保存下来,否则视为无效,会丢弃这个块重新下载。

BitTorrent 的延伸

BT下载严重依赖于 Tracker 服务器,那是不是意味着 Tracker 崩了整个 BT 网络就崩了,当然 Tracker 可能不止有一个,多个 Tracker 服务器可以增加 P2P 网络的容错性,那有有没有一种比较优雅的解决方案,来更好的解决这个问题呢?

为了摆脱对 Tracker 服务器的依赖,彻底去中心化,这时候,DHT 出现了,DHT全称分布式哈希表(Distributed Hash Table),是一种分布式存储方法。在不需要服务器的情况下,每个节点负责一个小范围的路由,并负责存储一小部分数据,从而实现整个DHT网络的寻址和存储,相当于所有人一起构成了一个庞大的分布式存储数据库。

在 DHT 网络中每个节点拥有两个角色:

  • 作为 BT 下载的节点,来进行上传和下载资源;
  • 作为DHT网络中的一员,作为一个小型 Tracker ,保存一部分其他 Peer 的地址信息;

DHT 的本质是把所有人都变成一个小型 Tracker,每个人都拿着一份动态更新的地址和文件信息。当需要进行下载的时候,先根据自己本地存的路由表找其他节点,其他节点再去找他们保存的其他节点,直到找到拥有文件的人。一传十十传百、千、万,最后通过N个人的中转,找到应该连上的人。

磁力链接

目前最流行的下载方式是磁力链接(Magnet URI scheme)就是基于 DHT 网络的,格式为magnet:?xt=urn:btih:<InfoHash>

其中 urn 为统一资源名称,btih 是 BitTorrent Info Hash 的缩写,是 BitTorrent 使用的 Hash 函数。除了 btih 还可以是其他类型的 Hash 函数,但不如 btih 用的多。这一串长度为 40 的字符串正是文件内容的 Hash,BT下载工具就根据这个 Hash 在DHT网络中定位下载文件。


©2018 - Felicx 使用 Stellar 创建
总访问 113701 次 | 本页访问 326
共发表 95 篇Blog · 总计 139.1k 字