DHT 网络是怎么工作的

2026-05-16 科普 P2P · 约 10 分钟

每次用 磁力古哥 搜到一个磁力链接,我都会想到一个问题:没有中心服务器,客户端是怎么找到拥有这个资源的其他人的?答案就是 DHT(Distributed Hash Table,分布式哈希表)。

DHT 到底是什么

简单说,DHT 是一个去中心化的键值存储网络。所有参与节点既是客户端也是服务器,每个节点存储一部分数据。你想要查一个 key 对应的 value,不需要问某个特定的服务器,而是问整个网络中"离这个 key 最近"的节点。

BitTorrent 使用的 DHT 实现基于 Kademlia 算法(BEP-5 规范),由 Petar Maymounkov 和 David Mazières 在 2002 年提出。这套算法后来被广泛用于各种 P2P 系统。

核心概念:节点 ID 和 XOR 距离

Kademlia 网络中,每个节点有一个 160 位的随机 ID(和 SHA-1 输出等长)。这个 ID 决定了节点在"地址空间"中的位置。

衡量两个节点"远近"的方式不是地理距离,而是 XOR(异或)运算

距离(A, B) = A ⊕ B

XOR 有几个很好的数学性质:

  • A ⊕ A = 0(自己到自己的距离是 0)
  • A ⊕ B = B ⊕ A(对称性)
  • A ⊕ B > 0(当 A ≠ B 时,距离严格大于 0)
  • 满足三角不等式

这些性质保证了 XOR 距离能作为有效的度量标准,而且计算极快——只是逐位异或。

k-bucket 路由表

每个 DHT 节点维护一张路由表,叫 k-bucket。它把整个 160 位地址空间按距离分桶:

桶编号距离范围存储内容
0[2⁰, 2¹)最近的节点(最多 k 个)
1[2¹, 2²)稍远的节点
2[2², 2³)更远的节点
.........
159[2¹⁵⁹, 2¹⁶⁰)最远的节点

每个桶最多放 k 个节点(通常 k=8 或 k=20)。满了怎么办?简单的策略是丢弃不活跃的节点,保留最近有通信的节点。

实际路由表不需要 160 个桶——大部分桶是空的,只有活跃的那些才占用内存。一个典型节点的路由表里有几百到几千个其他节点。

查找流程:get_peers 和 find_node

DHT 有两种核心操作,对应 BitTorrent 的需求:

find_node

给定一个目标 ID,找到离它最近的节点。步骤:

  1. 从自己的路由表中选出离目标最近的 α 个节点(α 通常取 3)
  2. 同时向这 α 个节点发 find_node 请求
  3. 收到回复后,把回复中的节点加入路由表
  4. 从所有已知节点中再选最近的未查询节点,继续发请求
  5. 直到没有更近的节点了(收敛)

这个过程的时间复杂度是 O(log n)——网络有 100 万个节点,大约 20 跳就能找到目标。

get_peers(核心)

这才是磁力搜索用到的关键操作。流程:

  1. 你想下载一个资源,有它的 info hash
  2. 向 DHT 网络发 get_peers(info_hash)
  3. 如果被问的节点知道谁有这个资源,直接返回 peer 列表
  4. 如果不知道,返回离这个 info hash 最近的其他节点
  5. 继续问这些节点,直到找到 peer 或遍历完

announce_peer:告诉网络你有资源

光能找还不行,还得有人"报到"。当你开始下载(或做种)一个资源时,你会向找到的节点发 announce_peer(info_hash),告诉它们:"我有(或正在获取)这个 info hash 对应的资源。"

收到 announce 的节点会在本地记录这条映射。下次别人用同一个 info hash 来 get_peers,它就能返回你的信息。

这就是 DHT 能"记住"谁有哪个资源的机制——没有全局数据库,只有每个节点本地维护的一小段映射。

DHT 网络的现实表现

说了这么多理论,实际用起来怎么样?我的经验:

  • 冷启动:第一次启动客户端,DHT 需要 10-30 秒来 bootstrap(从种子节点开始构建路由表)
  • 热门资源:秒级找到 peer,和 tracker 体验差不多
  • 冷门资源:可能要几十秒甚至找不到——前提是网络上至少有一个 peer 在线
  • 稳定性:节点频繁上下线,路由表需要持续维护

所以我做 磁力古哥 的时候,聚合多个磁力索引站就很有必要——DHT 爬虫抓取的信息不一定完整,多个源互为补充效果更好。

DHT 爬虫的原理

既然 DHT 网络上流通着大量的 get_peers 请求,每个请求都带 info hash,那只要在 DHT 网络上监听这些请求,就能收集到全网正在传播的资源指纹。这就是 DHT 爬虫的基本思路。

具体做法:

  1. 运行一个 DHT 节点,主动参与网络
  2. 监听收到的 get_peersannounce_peer 消息
  3. 提取其中的 info hash
  4. 对每个 info hash 发 BEP-9 metadata 请求,获取文件名和大小
  5. 建立索引数据库,对外提供搜索

大部分磁力搜索网站的底层都是这样运转的。它们不存储任何文件,只是在观察 DHT 网络的"流量指纹"。

快速方案:磁力古哥 一次搜索多个磁力站。免费无广告,Android 直接装。
下载 APK ↗ 了解功能