BitTorrent 分布式散列表(DHT)协议详解

概述

DHT 协议大幅度提高了 BitTorrent 网络的容错性,使整个网络难以因 Tracker 服务器的下线而崩溃,而这一切的外在表现就是“磁力链接”。

DHT 协议涉及到分布式散列表的相关知识,请参考分布式散列表协议 —— Kademlia 详解

下文将 ”BitTorrent 软件“ 简称为 “BT 软件“。

为什么需要 DHT 协议?

伙伴(Peer)协议可以让 BitTorrent 节点之间互相交换已知信息来完整下载和上传。当然这一切的前提是我们能够知道其它节点的联系方式,没有了这个前提,我们只能以 0KB/s 的速度等待了。Tracker 协议用于查询可以帮助下载和上传的节点,有了节点的联系方式我们才能通过伙伴(Peer)协议来交换数据完成下载和上传。但是 Tracker 是一个中心的服务器,一旦 Tracker 服务器下线,那么网络中的所有节点就只能抓瞎了。

可见 Tracker 服务器是整个 BitTorrent 网络的弱点,究其本质其实是因为节点的联系方式都存储在一个固定的位置,容错性很差。如果将这些信息存储在整个 BitTorrent 网络中,那么少数节点的故障并不会导致联系方式的丢失,也就不会影响整个网络的运行。这就是 DHT 协议的任务 —— 将每个节点变成一个小型 Tracker。

磁力链接

当你使用 BT 软件下载的时候你可能见过下面这样的地址。

magnet:?xt=urn:btih:c9e15763f722f23e98a29decdfae341b98d53056&dn=Cosmos+Laundromat&tr=udp%3A%2F%2Fexplodie.org%3A6969&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Ftracker.empire-js.us%3A1337&tr=udp%3A%2F%Ftracker.leechers-paradise.org%3A6969&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337&tr=wss%3A%2F%2Ftracker.btorrent.xyz&tr=wss%3A%2F%2Ftracker.fastcast.nz&tr=wss%3A%2F%2Ftracker.openwebtorrent.com&ws=https%3A%2F%2Fwebtorrent.io%2Ftorrents%2F&xs=https%3A%2F%2Fwebtorrent.io%2Ftorrents%2Fcosmos-laundromat.torrent

看起来很长,其实这个链接可以简化。

magnet:?xt=urn:btih:c9e15763f722f23e98a29decdfae341b98d53056

严格来说上面的已经是最简版本了,但是对于现在的 BT 软件,如果你输入一个含有 40 个十六进制字符的字符串,它会自动帮你转为磁力链接。

c9e15763f722f23e98a29decdfae341b98d53056

所以说最重要的就是这个十六进制字符串。你可以把用你的 BT 软件下载这段字符串,下载下来的视频还挺有趣的。

上面这段十六进制字符串是通过哈希算法生成,每份资源丢到哈希算法里都会得到这样的一个字符串,并且仅属于这份资源(大概),也就是我们在之前的系列文章中提到的 info_hash

工作流程

当节点上线时它会随机生成一个节点 ID 并作为自己的 ID 使用。

初始状态下我们并不知道其它节点的联系方式,此时需要通过其它方式或者,比如 Trakcer 服务器,或者 .torrent 文件中写明的联系方式。

然后我们会与节点建立联系并构建路由表,并周期性地测试节点的联通状态,比如通信时延。通过此种方式我们获得了一个通信质量良好的节点列表。这时伙伴交换(Peer Exchange)协议也开始工作,我们会和越来越多的节点建立联系。

当我们想下载一个文件时,我们计算文件 ID 和已知节点之间的距离,从自己的路由表中选择距离最近的若干个节点发起查询请求,接收到请求的节点如果正好可以提供这个文件则可以直接开始下载,反之它们会从自己的路由表中选择距离最近的若干个节点返回。

当我们向上传某些文件时(做种),我们会计算文件的 ID,然后将自己的联系方式存储到距离文件 ID 最近的若干个节点中。

人话

我们做种时总会把自己的联系方式存储到一个很小的范围内,此范围由文件的 ID 决定。当我们想下载时,会根据文件 ID 计算出这个范围,然后从里找做种的人就行了。

协议概述

DHT 协议是一个基于 Kademila 协议并使用 UDP 传输的应用协议,它可以将每个节点变成一个小型 Tracker,避免中心化的 Tracker 服务器所带来的风险。

DHT 协议的基础部分和 Kademila 协议一致,比如节点的 ID 都是 160 位的二进制数,使用异或算法计算距离和使用相同方法构建路由表。不同的地方在于 DHT 协议更改了部分 RPC 过程,并将这些 RPC 集合命名为 KRPC 协议。

KRPC 协议

KRPC 协议是一个由 UDP 数据包和其包裹的经过 B 编码字典组成的简洁的 RPC 协议。每次查询只会有一次响应,没有重发/重试这种机制。

下面是三种消息类型。

  • query(查询)
  • response(响应)
  • error(错误)

下面是四类查询。

  • ping
  • find_node
  • get_peer
  • announce_peer

KRPC 消息是一个经过 B 编码的字典(dictionary),每个消息至少有三个 key,分别表示”事务 ID“、”消息类型“ 和 ”客户端名称和版本“。根据消息的不同可以有更多的 key。

ping

此消息用于检查目标节点的状态,发起者应发送自己的节点 ID,接收者应回复自己的节点 ID。

喂?听到了么?

听到了。

find_node

此消息用于查询指定节点的联系方式(IP 地址和端口),发起者发送目标节点的 ID,接收者会从自己的路由表中选出距离目标节点最近的 k 个节点 ID 并返回。这是一个递归的过程,详情请阅读分布式散列表协议 —— Kademlia 详解

get_peers

此消息用于查询于指定的 info_hash 相关的节点的联系方式,功能类似 Trakcer 服务器。发起者应发送 info_hash,接收者应返回与 info_hash 有关的节点列表,如果接收者不知道哪些节点于 info_hash 有关,则从自己的路由表中选出 k 个距离 info_hash 最近的节点并返回。

announce_peer

当一个节点于某个 info_hash 有关时(下载或上传),它应当发起此消息。发起者应发送自己的节点 ID、info_hash 和监听端口。接收者应存储接收到的联系方式,将 info_hash 和联系方式关联起来,用于响应 get_peers 消息。

参考资料

DHT Protocol

本文作者:ADD-SP
本文链接https://www.addesp.com/archives/5428
版权声明:本博客所有文章除特别声明外,均默认采用 CC-BY-NC-SA 4.0 许可协议。
暂无评论

发送评论 编辑评论


上一篇
下一篇