磁力链接不是下载地址,而是用文件内容算出来的一串”数字指纹”。btdad 这类磁力搜索站点只用几十个字符就能定位到一个资源,靠的不是某台中心服务器,而是 BitTorrent 的 infohash 加 DHT 分布式网络。把这层原理拆开,就能解释两个长期困扰新手的现象:有的链接一直卡在”获取元数据”,有的显示名和真实内容对不上。

磁力链接到底是什么
磁力链接(Magnet URI)是一种按内容寻址的标识符,严格说不属于 URL。URL 回答的是”去哪台机器取”,磁力链接回答的是”取什么东西”——它由文件内容的哈希值构成,任何持有该文件的人都能独立算出同一串值,不需要中心机构发放。
这个标准的草稿出现于 2002 年,初衷是把 eDonkey2000 的 ed2k: 与 Freenet 的 freenet: 两种 URI 格式做”厂商与项目中立化”的统一,并尽量贴近 IETF 官方的 URI 标准。一条典型磁力链接如下(为便于阅读做了换行,实际是一整行):
magnet:?xt=urn:btih:c12fe1c06bba254a9dc9f519b335aa7c1367a88a
&dn=Big+Buck+Bunny
&tr=udp%3A%2F%2Ftracker.example.com%3A6969
参数之间顺序无关,格式与 HTTP 末尾的查询字符串相同。解析时按 & 切分,每个键值对按第一个 = 切分(tracker 地址自身可能含 =,不能多切),值再做 URL 解码:%3A 还原成冒号,+ 还原成空格。常见参数的含义如下。
| 参数 | 全称 | 作用 | 是否必填 |
|---|---|---|---|
| xt | exact topic | 内容的哈希 URN,如 urn:btih: 加 40 位十六进制 | 必填 |
| dn | display name | 显示名,仅作界面提示 | 选填 |
| tr | tracker | tracker 服务器地址,可重复出现 | 选填 |
| xl | exact length | 文件总字节数提示 | 选填 |
| ws / xs | webseed / exact source | HTTP 直连下载的备用地址 | 选填 |
这张表里有个容易踩的坑:dn 只是客户端显示用的提示,可以随意伪造,真正决定身份的只有 xt。搜索结果里那个漂亮的文件名,和哈希算出来的究竟是什么内容,两者之间没有任何绑定关系。
infohash 是怎么算出来的
infohash 是对种子文件里的 info 字典做 SHA-1 得到的 20 字节摘要,写成十六进制就是 40 个字符。info 字典里放着文件名、大小、分片长度(piece length)和每个分片的哈希串,唯独不含真实数据本身——哈希只覆盖元数据,改动其中任意一个字节,整串 infohash 都会跟着变。想自己验证,用下面这段脚本可以直接算出来:
import hashlib, bencodepy # 安装:pip install bencodepy
with open("demo.torrent", "rb") as f:
meta = bencodepy.decode(f.read())
info = bencodepy.encode(meta[b"info"])
infohash = hashlib.sha1(info).hexdigest()
print(infohash) # 40 位十六进制,即磁力链接里 urn:btih: 后面的那串值
BitTorrent v1 与 v2 在哈希方案上的差异,值得单独拉出来对比。
| 维度 | v1 | v2 |
|---|---|---|
| 哈希算法 | SHA-1 | SHA-256 |
| 摘要长度 | 20 字节(40 位十六进制) | 32 字节(64 位十六进制) |
| 磁力前缀 | urn:btih: | urn:btmh: |
| 分片校验 | 分片哈希平铺存放 | Merkle 树,叶节点固定 16 KiB 块 |
| 文件粒度 | 整个种子一个哈希 | 每个文件单独成树,可跨种子去重 |
v2 改用 SHA-256 的直接原因,是 SHA-1 已被证实存在碰撞风险。为了兼容只认 20 字节哈希的 DHT 与 tracker 报文结构,v2 在与这两类组件通信时会把 SHA-256 结果截断到 20 字节。v2 还引入了混合种子(hybrid torrent),同时携带两套哈希,让新老客户端能进同一个网络。
没有 tracker 时怎么找到同伴
DHT 网络替掉了 tracker 的角色,让每个客户端自己成为一台”小型 tracker”。BitTorrent 的 DHT 实现定义在 BEP-5 中,基于 Kademlia 算法跑在 UDP 之上,用四类 RPC 报文通信:ping、findnode、getpeers、announce_peer。它的寻址逻辑可以压缩成三句话:
- 每个节点随机生成 160 位 Node ID,与 infohash 处在同一个 160 位地址空间;
- 节点之间的”距离”用异或(XOR)计算,结果越小代表在地址空间上离目标哈希越近;
- 查询 get_peers 时,被问到的节点要么返回持有该资源的 peer 列表,要么返回它知道的、离目标更近的节点,请求方不断迭代逼近,直到找不到更近的节点为止。
路由表按距离前缀分成多个 k-bucket 维护,15 分钟内响应过请求的节点算”好节点”,连续多次不响应的会被剔除。为防止恶意节点替别人登记资源,announcepeer 必须带上此前 getpeers 拿到的 token,token 由被查询节点按 IP 生成并设有有效期。到这里,”不需要中心服务器”这句话才算有了具体的技术落点。
磁力搜索站点在这条链路上做什么
磁力搜索站点本身不存文件,它是一个”infohash 采集器 + 元数据索引器”。从一条磁力链接到一条可检索的索引,链路大致是这样走的:
- 在 DHT 网络里维持大量节点身份,监听 announce_peer 报文,持续收集 infohash;
- 对收集到的 infohash 主动发 get_peers,拿到正在做种或下载的 peer 地址;
- 与 peer 建立 TCP 连接,先做 BitTorrent 握手,在保留位里声明支持扩展协议(BEP-10);
- 通过扩展握手协商出 ut_metadata,再按 16 KiB 一片把 info 字典拉回来(BEP-9);
- 对拼装好的元数据做哈希校验,与 infohash 比对一致后才入库建索引。
第 5 步是整条链路的安全支点:infohash 是内容地址,peer 无法伪造一份哈希值恰好匹配的元数据。这也是为什么”论坛里贴的 40 个字符”在技术上与原始种子文件等价。反过来看,这条链路也解释了两个常见困惑——冷门资源搜不到,是因为没有任何 peer 在线,元数据无从获取;显示名与内容不符,是因为 dn 本就是未经验证的提示。
技术本身中立,边界要自己划清
磁力链接是中性技术,Linux 发行版镜像、开源数据集、游戏客户端补丁都在用它分发,能显著降低发布方的带宽压力。是否侵权取决于所传播内容的授权状态,与链接格式无关。
需要提醒的是,BitTorrent 协议本身不提供匿名性。同一个 swarm 里的参与者通常能看到彼此的 IP 地址。因此使用这类技术时,遵守所在地区的法律法规与内容授权要求,是使用者自己的责任。
常见问题(FAQ)
Q1:磁力链接和种子文件有什么区别?
种子文件自带完整元数据,磁力链接只带哈希,元数据需要从 DHT 网络里的 peer 处现取。
Q2:磁力链接为什么一直卡在获取元数据?
说明没找到持有该 infohash 元数据的在线 peer,网络里暂时没有可用来源。
Q3:磁力链接能隐藏下载者的 IP 吗?
不能。BitTorrent 协议本身不匿名,同一 swarm 内的参与者可以看到彼此的 IP 地址。