近几年的版权大战,影视资源基本集中到了爱奇艺,腾讯和优酷三家手中,其他的盗版资源大站基本都被干了。21世纪前10年的”互联网是免费“的黄金时代,随着快播等的倒下,基本上已经结束了。
我并不抵制视频网站的付费模式,作为商业公司,它们花费高昂的版权和带宽费用,为我们提供高清的视频,通过广告和付费会员的模式来盈利,是正常不过的商业行为。
但存在的一些问题,让我在观看影视作品时,会选择bt下载。
- 资源分散,三家各自购买了一些独家的版权资源,这样用户需要开三家的付费会员。
- 资源缺失,例如,很多美剧,由于尺度问题,无法引进国内。
- 资源被删减,广电剪刀手的鬼斧神工,把电影剪的支零破碎。当年在电影院看《金刚狼3》,最后森林里的大决战,狼叔刚打完一瓶药,准备大开杀戒,然后下个镜头气喘吁吁地在跑步,看的我和我的小伙伴一脸懵逼。
- 好吧,还有vvvv...vvvip。
BT下载的原理
bt是一种用于分发文件的协议。相比传统的http下载,bt下载每个客户端在下载文件同时,也将文件上传给其他的客户端。

可以看出传统的http下载方式,瓶颈在中心的服务器,越多的客户端向服务器发起下载请求,服务器就需要更多的带宽和cpu等服务器资源。

先解释下图里的几个术语。
- Torrent File- bt种子文件(下面的章节,会一起看下bt文件里面的内容)
- Web Server- 保存bt种子的网站服务器
- Tracker- bt服务器,帮助bt客户端获取其他正在下载相同文件的bt客户端信息
- Peer- bt客户端,下载或者上传文件
Bt下载的过程
- 从资源网站,如人人影视等下载bt种子。
- 本地启动bt客户端(uTorrent等),解析bt种子,得到tracker服务器<ip,port>, 向tracker发起请求。
- tracker响应请求,将正在下载相同资源的其他bt客户端信息- <ip, port>,返回给我们。
- 发起对其他bt客户端的连接,从其他bt客户端下载文件,同时将已下载好的文件,上传给其他的bt客户端。
可以看出作为中心服务器的tracker, 不需要保存文件,也不提供文件的下载,只保存正在下载文件的bt客户端的信息。相比http下载的服务器,一台tracker服务器可以同时支持更多的客户端。
Bt种子
详细来看看bt种子文件里的内容
bt种子文件,使用了bt定义的bencoding来编码文件内容,是一个bencoing编码的字典。里面的键值对有:
announce
    tracker服务器的url地址
info
    name
        建议的文件名
    piece length
        整个文件分割成多个相同大小的文件块后,文件块的大小(单位:byte)
    pieces
        每个文件块的sha1值(长度20的字符串),拼接在一起的字符串。
下面的length和files不会同时出现在文件里,只出现length时,表示我们下载的是单独的一个文件,这时候上面的name的值是文件名。只出现files时,表示我们下载的是保存在目录里的一组文件,上面的name的值是目录名。
    length
        文件的大小(单位:byte)
    files
        目录里的文件列表
        length
            文件大小(单位:byte)
        path
            子目录的名字单文件的情况
{
    "announce":"xxxx",
    "info":{
        "name":"xxxx",
        "piece length":"xxxx",
        "pieces":"xxxx",
        "length":"xxxx"
    }
}多个文件的情况
{
    "announce":"xxxx",
    "info":{
        "name":"xxxx",
        "piece length":"xxxx",
        "pieces":"xxxx",
        "files":[
            {
                "length":"xxxx",
                "path":"xxxx"
            },
            {
                "length":"xxxx",
                "path":"xxxx"
            },
            {
                "length":"xxxx",
                "path":"xxxx"
            }
        ]
    }
}请求Tracker
解析bt种子后,我们可以从announce键得到tracker的地址,向tracker发起一个get请求来获取,正在下载同一资源的其他bt客户端的信息。
Get请求有以下的参数
| 参数 | 含义 | 
|---|---|
| info_hash | 这个参数标识了资源,每个资源都有唯一的info_hash,info_hash是对种子文件里 info键对应的值使用sha1哈希来生成。 | 
| peer_id | bt客户端启动时,会随机生成一个长度为20的字符串,作为客户端id | 
| ip | bt客户端的ip地址 | 
| port | bt客户端正在监听的端口 | 
| uploaded | bt客户端已经上传的文件大小 | 
| downloaded | bt客户端已经下载的文件大小 | 
| left | bt客户端还要下载的文件大小 | 
| event | 事件, start-bt客户端开始下载,completed-bt客户端完成下载,stopped-bt客户端终止下载,empty-bt客户端常规的轮询请求,用来告诉tracker,客户端仍然在线 | 
Get请求的正常响应包含两个字段
| 字段 | 含义 | 
|---|---|
| interval | bt客户端常规轮询请求tracker的时间间隔。bt客户端可以不遵守这个时间间隔,在收到的其他bt客户端数量不够时,可以自行决定是否马上重新请求tracker | 
| peers | 其他bt客户端信息的列表 | 
DHT-分布式哈希
因为bt往往是用来下载盗版资源,所以国家也在大力打击bt下载。从上面的内容,你应该知道怎样去摧毁一个bt下载了吧。
tracker作为整个bt下载的中心,只要让关停tracker所在的服务器,我们的bt客户端就无法得到其他客户端的信息了。
bt社区也很早意识到这个问题,在bt客户端了加入DHT(分布式哈希表),来实现无中心化,将原来由tracker存储的信息,分散存储到整个bt网络的每一台客户端里,这样避免了tracker的单一故障带来的整个网络瘫痪。bt下载网络从第一代包含tracker服务器的网络,转换成了第二代的无tracker的dht网络。
实现DHT的算法和技术有很多,bt和bt的门徒们(eMule等)使用的都是基于Kademlia算法,我之前写过一篇关于kademlia算法的博客,可以先看看。
简单地讲下整个查找过程。
- 每一个bt客户端都有一个随机的160 bit的node_id。每个资源都有一个唯一的info_hash。
- 每一个bt客户端都维护个一个路由表,里面保存着其他bt客户端的信息
<ip, port, node_id>。- 本地bt客户端,在自己的路由表里选出8个离资源info_hash
最近的bt的客户端,发送get_peers请求,询问这8个bt客户端上,是否保存有正在下载这个资源的其他客户端信息。- 如果有,那就响应返回给本地bt客户端,如果没有,那就从路由表选出8个离资源info_hash
最近的bt的客户端信息,返回给本地bt客户端。- 如果返回的是正在下载这个资源的其他客户端信息,本地bt客户端就可以开始连接下载。
- 本地bt客户端收到8个离资源info_hash
最近的bt的客户端信息,向这8个客户端再发起get_peers请求。- 这个过程,一直递归持续下去,直到无法找到离资源info_hash
更近的客户端。整个过程下来,最终本地bt客户端会得到8个离资源最近的客户端信息。- 本地bt客户端向这8个离资源最近的客户端,发送
announce_peer,告诉它们,我正在下载这个资源,以后可以连接我来下载资源。
类比下生活中例子。
川普手里应该有美国51区的机密档案,我想通过电话联系上川普,拿到机密档案。但我没有川普的电话(⊙︿⊙)。我先在通讯录里找到最有可能认识川普的8个朋友(这里面有美国友人,政府官员,这些人应该比普通人,更有可能有川普的电话),分别打电话询问他们。
他们应该是没有机密档案,他将他通讯录里最有可能认识川普的8个朋友的电话给我。我要继续打电话给8个人,询问他们有没有川普电话。
将这个过程不断地持续下去,根据六度分割理论,最后我肯定能拿到川普的电话,川普答应将机密文件发给我。
整个过程下来,我拿到很多人的电话号码,拿到机密档案后,我会告诉这里面和川普"熟悉度"的8个人,我拿到机密档案了。以后找我也可以拿到机密档案。
当然,我们利用上面的流程,除了51区的机密文件,当然也可以用来得到其他的文件。
种子爬虫的原理
种子爬虫正是利用了上面的过程。
- ”机密档案“ - 资源info_hash(这里的”机密文件“可以理解为只是一个代号,而不是机密文件里的内容,用来表明我要拿的东西是机密文件。就好像info_hash是资源的标识,而不是资源的具体内容)
- 机密档案的内容 - 其他正在下载这个资源的客户端信息 (这里可能有点类别不太恰当,bt dht拿到是正在下载这个资源的其他客户端信息,具体的文件内容,需要本地客户端连接上其他客户端去下载)
- 通讯录 - 路由表
- 电话号码 - 路由表里的其他bt客户端信息
因为资源的info_hash是通过sha1算法计算种子文件里的内容得到的,我们得到info_hash后,后面有办法通过info_hash得到种子文件。所有我们的种子爬虫收集的是资源的info_hash。
为了得到更多的info_hash,我们需要将自己加入更多人的路由表(通讯录),同时声明自己离资源更近(认识川普),这样有更大的概率,会被挑选出来,返回给请求者,请求者会带着资源info_hash的参数询问我们,你有没有这个资源相关的信息。这样我们就得到了info_hash了。
种子爬虫具体的实现-KRPC协议
krpc协议在bep5已经详细讲解,可以直接看文档。
种子爬虫主要用到是find_node和get_peer。
我们带着自己的node_id,不断地向其他的bt客户端发起find_node请求,其他客户端收到请求,会将收到的node_id插入到路由表中(就好像将自己的电话号码加入到他的通讯录),同时我们要不断伪造不同的node_id, 插入到其他bt客户端的路由表,这样后面其他bt客户端在自己路由表,挑选哪些node_id对应的bt客户端上,离资源更近时,更有可能将我们的node_id挑选出来,返回给请求者。(就好像我要装得我和川普很熟悉,我比其他人,更可能有川普的电话)。
加入到其他客户端的路由表后,我们就可以等待接受get_peers请求,get_peers请求里会带有参数info_hash,这样就可以收集资源了。
我们的种子爬虫还有几个需要注意的地方:
- 客户端刚启动的时候,路由表里什么信息都没有,需要向常驻网络的节点(如dht.transmissionbt.com:6881),发起对本客户端node_id的find_node请求,在这个过程里,将收到的其他客户端信息填充到自己路由表。
- 要响应ping,find_node,get_peer和announce_peer的请求,路由表是有大小的,如果不响应,路由表会将你删除。就好像打你电话,你总是不接,那就把你从通讯录里删掉了。
- 因为NAT的原因,种子爬虫要部署到有公网ip的机器上。
具体代码实现
今天就写到这里,有点累了,具体代码实现,下一篇文章,我会详细讲解下。
参考资料
BEP3
BEP5
Kademlia: A Peer-to-peer Information System Based on the XOR Metric
Bit torrent techtalks_dht
Youtube - BitTorrent Tech Talks: DHT

