前言
经常在网上飙车的老司机应该都知道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 | { |
一个种子文件中的 info.pieces 字段包含了每个小块的哈希值,而每个块的大小都是 256KB。这样就相当于把下载任务分解了,分解成了下载若干个大小为 256KB 的小块的任务。采用并发的方式去依次请求每个块的数据,然后计算好数据的位置放进最终结果中返回,然后把内存中的数据写入硬盘。
BitTorrent 工作全流程
简单讲下BT下载的整个流程
- 种子发布者制作种子,且向 Tracker 服务器表明,大家要下载这个种子就来找我。(Tracker 的地址就是种子文件中 announce 字段中的 url);
- 种子发布者把做好的种子分享到互联网;
- 下载者在互联网上获取到种子文件;
- 下载者本地的 BT 客户端解析种子文件,拿到 Tarcker 地址,向 Tarcker 发起请求(HTTP或UDP),获取其他 Peer 的地址;
- Tracker接收到请求后,去自己的存储里找拥有这个种子中的文件的 peers 的 IP:port,返回给下载者,并且把当前下载者的 IP:Port 加入服务器的存储;
- 下载者与其他 Peer 建立连接,由于一个文件被分成了若干个文件块,所以下载者可以和多个 Peer 下载不同的块,下载完成后,校验块的哈希值,保存在本地。(这也是下载种子的人越多,下载速度越快的原因);
- 整个文件下载完成时,校验整个文件哈希值,不出意外,下载成功;
- 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网络中定位下载文件。