BT/DHT 相关
摘录:https://zhuanlan.zhihu.com/p/34377702
https://github.com/shiyanhui/dht/wiki/
http://www.aneasystone.com/archives/2015/05/how-does-magnet-link-work.html
BT协议其实是一个协议簇,BEP-3 是其基本协议内容,其他的大部分都是围绕这个来进行扩展或补充。要想从BT网络中下载一个资源,必须具备以下部分:
种子文件(也就是我们常说的种子,后缀是.torrent,本质上是一个由bencode编码的文本文件,其把资源分成很多虚拟块,并记录每个块的hash值,另外上面还记录着其他信息,比如文件大小、名字、Tracker服务器等)
BT客户端(需要有专门解析BT协议的程序,这样才能下载,比如迅雷,电驴)
Tracker服务器 (记录着peer和种子相关信息,起着中心调控的作用)
下载资源的时候,客户端首先根据bencode(bencode是BT协议中的编码方式)解码种子文件,得到Tracker服务器的地址和资源信息,通过和Tracker服务器沟通得到其他已经下载该资源的peers信息(其他已经拥有该资源的客户端或者发布该资源的人),然后再和这些peers沟通得到自己想要的部分,即互通有无。由于把文件分成很多块来同时从不同的地方下载,这也就是为什么BT通常下载快的原因。
BT协议簇中的DHT协议 是基于 Kademlia协议 建立的,其基本思想很好理解。DHT 由很多节点组成,每个节点保存一张表,表里边记录着自己的好友节点。当你向一个节点A查询另外一个节点B的信息的时候,A就会查询自己的好友表,如果里边包含B,那么A就返回B的信息,否则A就返回距离B距离最近的k个节点。然后你再向这k个节点再次查询B的信息,这样循环一直到查询到B的信息,查询到B的信息后你应该向之前所有查询过的节点发个通知,告诉他们,你有B的信息
BT种子嗅探器就是利用了DHT协议得到peer信息后会向他之前查询过的节点发送通知这一点,这就是嗅探器的核心。
剩下的工作就是我们要让更多的节点发给我们通知。那么如何让更多的节点发给我们通知呢?
我们要不断的查询自己的好友节点表,并对返回回来的节点进行查询,这样才会有更多的人认识我们
别人向我们查询Target的时候,我们要伪装成Target的好友,返回结果里边包括自己,这样会有更多被查询、收到通知的机会
这就是BT种子嗅探器的原理,简单吧 :)
种子下载器
在BT网络中,通过上述原理收到信息并不是种子,而是发送消息者的ip和port、种子infohash(可以理解为种子的id)。我们如果想要得到种子的话,还需要做一番工作。这里涉及到另外一个非常重要的协议 BEP-09,BEP-09规定了如何通过种子infohash得到种子。
首先同我们收到的消息里边的 ip:port 建立TCP连接,然后发送握手消息,并告知对方自己支持BEP-09协议,然后向对方请求种子的信息,收到对方返回的种子信息后,依次或同时请求每一个块。最有所有块收集完后,对其进行拼接并通过sha1算法计算其infohash,如果和我们请求的infohash值相同则保存起来,否则丢掉。
磁力链接
在网络中定位资源最简单的方法是URL(统一资源定位符),它是通过资源的位置来进行定位。而在DHT网络中,则是使用URN(统一资源名称)来进行定位,磁力链接就是基于文件内容的散列函数值来链接到特定文件,生成一个唯一的文件识别符,从而在DHT网络中定位并下载文件。 一个最简单的磁力链接格式如下:
magnet:?xt=urn:btih:51df6808c739174c8f264701ba94460c5238d6ce
其中urn为统一资源名称,btih是BitTorrent Info Hash的缩写,是BitTorrent使用的Hash函数。除了btih还可以是其他类型的Hash函数,但不如btih用的多。这一串长度为40的字符串正是文件内容的Hash,BT下载工具就根据这个Hash在DHT网络中定位下载文件。
Torrent(种子)就保存了一个文件的一些信息,名字/长度/子文件目录/子文件长度等信息,其中最重要是拥有该文件的peers服务器,也因此,可以通过种子,向这些peers发送下载请求.下载到文件.
但是在DHT(分布式哈希表)出现之前,所有节点都需要连接到Tracker服务器,以获取到拥有某个文件的peers(或者Torrent).
DHT协议基于udp通讯,规定每个node(遵守BitTorrent协议,未实现提供下载功能的服务器)内部存储一个RoutingTable(路由表).该表存储了其他的node(或peer)节点.
每个node的信息包括nodeId(随机的20个byte/ip/port(其中ip/port在udp通讯的包中都已经携带,所以,实际上最重要的就是nodeId)).
而且定义了几种方法进行node间的交互(此处设A节点为请求方,B节点为响应方, 这些方法就是发送对应规则的请求响应报文,例如Http的GET/POST/DELETE等)
ping: A向B发送请求,测试对方节点是否存活. 如果B存活,需要响应对应报文
find_node: A向B查询某个nodeId. B需要从自己的路由表中找到对应的nodeId返回,或者返回离该nodeId最近的8个node信息. 然后A节点可以再向B节点继续发送find_node请求
get_peers: A向B查询某个infoHash(可以理解为一个Torrent的id,也是由20个字节组成.该20个字节并非随机,是由Torrent文件中的metadata字段(该字段包含了文件的主要信息,也就是上文提到的名字/长度/子文件目录/子文件长度等信息,实际上一个磁力搜索网站提供的也就是这些信息).进行SH1编码生成的). 如果B拥有该infoHash的信息,则返回该infoHash的peers(也就是可以从这些peers处下载到该种子和文件). 如果没有,则返回离该infoHash最近的8个node信息. 然后 A节点可继续向这些node发送请求.
announce_peer: A通知B(以及其他若干节点)自己拥有某个infoHash的资源(也就是A成为该infoHash的peer,可提供文件或种子的下载),并给B发送下载的端口.
此外,node间的距离是通过nodeId进行异或计算的(也就是160个bit间进行异或)得出一个值,值越小,则距离越近. 由此可得出,越是高位的bit不相同(异或值为1),则值越大,距离越远(因为假设两个nodeId第1位就不同,其异或值必然大于2^160).
DHT出现之后,假设一个新的节点想要加入该网络,只需要获取到已经在网络中的任何一个node信息,向其发送find_node请求即可.想要获取某个info_hash的peer,也可直接发送get_peers,而无需连接到Tracker服务器.
github:
https://github.com/xiaojiong/DhtCrawler
https://github.com/shiyanhui/dht
https://github.com/BrightStarry/zx-bt