近几年的版权大战,影视资源基本集中到了爱奇艺,腾讯和优酷三家手中,其他的盗版资源大站基本都被干了。21世纪前10年的”互联网是免费“的黄金时代,随着快播等的倒下,基本上已经结束了。
我并不抵制视频网站的付费模式,作为商业公司,它们花费高昂的版权和带宽费用,为我们提供高清的视频,通过广告和付费会员的模式来盈利,是正常不过的商业行为。
但存在的一些问题,让我在观看影视作品时,会选择bt下载。

  • 资源分散,三家各自购买了一些独家的版权资源,这样用户需要开三家的付费会员。
  • 资源缺失,例如,很多美剧,由于尺度问题,无法引进国内。
  • 资源被删减,广电剪刀手的鬼斧神工,把电影剪的支零破碎。当年在电影院看《金刚狼3》,最后森林里的大决战,狼叔刚打完一瓶药,准备大开杀戒,然后下个镜头气喘吁吁地在跑步,看的我和我的小伙伴一脸懵逼。
  • 好吧,还有vvvv...vvvip。

BT下载的原理

bt是一种用于分发文件的协议。相比传统的http下载,bt下载每个客户端在下载文件同时,也将文件上传给其他的客户端。

35fm3d.md.jpg

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

35hTe0.png

先解释下图里的几个术语。

  • Torrent File - bt种子文件(下面的章节,会一起看下bt文件里面的内容)
  • Web Server - 保存bt种子的网站服务器
  • Tracker - bt服务器,帮助bt客户端获取其他正在下载相同文件的bt客户端信息
  • Peer - bt客户端,下载或者上传文件

Bt下载的过程

  1. 从资源网站,如人人影视等下载bt种子。
  2. 本地启动bt客户端(uTorrent等),解析bt种子,得到tracker服务器<ip,port>, 向tracker发起请求。
  3. tracker响应请求,将正在下载相同资源的其他bt客户端信息<ip, port>,返回给我们。
  4. 发起对其他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_idbt客户端启动时,会随机生成一个长度为20的字符串,作为客户端id
ipbt客户端的ip地址
portbt客户端正在监听的端口
uploadedbt客户端已经上传的文件大小
downloadedbt客户端已经下载的文件大小
leftbt客户端还要下载的文件大小
event事件,start-bt客户端开始下载,completed-bt客户端完成下载,stopped-bt客户端终止下载,empty-bt客户端常规的轮询请求,用来告诉tracker,客户端仍然在线

Get请求的正常响应包含两个字段

字段含义
intervalbt客户端常规轮询请求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算法的博客,可以先看看。

分布式哈希表与Kademlia算法

BEP5-DHT Protocol

简单地讲下整个查找过程。

  1. 每一个bt客户端都有一个随机的160 bit的node_id。每个资源都有一个唯一的info_hash。
  2. 每一个bt客户端都维护个一个路由表,里面保存着其他bt客户端的信息<ip, port, node_id>
  3. 本地bt客户端,在自己的路由表里选出8个离资源info_hash最近的bt的客户端,发送get_peers请求,询问这8个bt客户端上,是否保存有正在下载这个资源的其他客户端信息。
  4. 如果有,那就响应返回给本地bt客户端,如果没有,那就从路由表选出8个离资源info_hash最近的bt的客户端信息,返回给本地bt客户端。
  5. 如果返回的是正在下载这个资源的其他客户端信息,本地bt客户端就可以开始连接下载。
  6. 本地bt客户端收到8个离资源info_hash最近的bt的客户端信息,向这8个客户端再发起get_peers请求。
  7. 这个过程,一直递归持续下去,直到无法找到离资源info_hash更近的客户端。整个过程下来,最终本地bt客户端会得到8个离资源最近的客户端信息。
  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_nodeget_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_idfind_node请求,在这个过程里,将收到的其他客户端信息填充到自己路由表。
  • 要响应ping, find_node, get_peerannounce_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

Last modification:March 26th, 2020 at 11:42 am
如果觉得我的文章对你有用,请尽情赞赏 🐶