DHT 网络是怎么工作的
每次用 磁力古哥 搜到一个磁力链接,我都会想到一个问题:没有中心服务器,客户端是怎么找到拥有这个资源的其他人的?答案就是 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,找到离它最近的节点。步骤:
- 从自己的路由表中选出离目标最近的 α 个节点(α 通常取 3)
- 同时向这 α 个节点发
find_node请求 - 收到回复后,把回复中的节点加入路由表
- 从所有已知节点中再选最近的未查询节点,继续发请求
- 直到没有更近的节点了(收敛)
这个过程的时间复杂度是 O(log n)——网络有 100 万个节点,大约 20 跳就能找到目标。
get_peers(核心)
这才是磁力搜索用到的关键操作。流程:
- 你想下载一个资源,有它的 info hash
- 向 DHT 网络发
get_peers(info_hash) - 如果被问的节点知道谁有这个资源,直接返回 peer 列表
- 如果不知道,返回离这个 info hash 最近的其他节点
- 继续问这些节点,直到找到 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 爬虫的基本思路。
具体做法:
- 运行一个 DHT 节点,主动参与网络
- 监听收到的
get_peers和announce_peer消息 - 提取其中的 info hash
- 对每个 info hash 发 BEP-9 metadata 请求,获取文件名和大小
- 建立索引数据库,对外提供搜索
大部分磁力搜索网站的底层都是这样运转的。它们不存储任何文件,只是在观察 DHT 网络的"流量指纹"。