如何用java实现一个p2p种子搜索(1)-概念

前言

<font size=2>说句大实话,网上介绍怎么用java实现p2p种子的搜索这种资料不是特别多,大部分都是python的,用python的话就会简单不少,它里面有不少简单方便的包,libtorrent等等,固然你用这些包能够实现功能,可是它封装了太好,以至于你很难知道里面的细节。为了深刻了解,而后我就用java实现了一把,固然中间遇到了不少的问题,也参考了github的项目。java

说到p2p,我想你们可能都用种子下载过文件,比较常见的就是.torrent结尾的文件,经过种子能够下载到种子对应的文件。node

基本概念

那么什么是种子呢?种子其实就是一个文件里面保存的是一个字典,其中最重要的一个字段就是info字段,里面保存文件名和文件长度。还有一个比较重要的字段是announce,这个是tracker地址经过这个地址能够查找到这个文件在哪些peer上面。还有一些种子没有announce字段,这样的种子被称为trackerless torrent,会有nodes字段来代替。python

除了种子能够下载文件,还有一种磁力连接也能够下载文件。那么什么是磁力连接呢?just like magnet:?xt=urn:btih:19838A8C4DE7DC2E34382249C9A52CFD9E3BB41Agit

复制这个连接,打开迅雷会让你下载权利的游戏,磁力连接的前面部分是不变的,也就是说最后那一长串字符串才是对应了你下载的资源。<font color=red >最后一长串的字符串叫作infohash,每一个种子都会对应一个infohash,因此磁力连接就是根据infohash来下载种子对应的文件。因此咱们只要收集到足够多的infohash,而后根据infohash再找到那些文件,创建起infohash和文件的对应关系,那么咱们的搜索也就完成了,这里其实作的是磁力的搜索。</font>github

好了如今咱们须要解决两个问题,第一个是怎么爬取infohash,第二个是怎么经过infohash来找到种子包含的文件名。网络

那么咱们怎么可以获取到infohash呢?这里以bittorrnt dht protocol为例less

<font color=red >要想获取infohash就必须成为dht网络中的一员,dht网络中由不少node组成,每一个node由160个bit组成。每一个node呢,都有存储其中一部分node的信息,这个信息呢包括node的id,ip,port等等,在存储node id的时候也不是随便存储,会按照距离的远近来存储,这里的距离不是物理的距离 ,而是逻辑距离。经过两个id的异或来计算。举个小例子 node a 的id是1110, node b的id等于0110,那么node a和node b之间的距离就是2的3次方。node的信息都存在路由表里面,每一个路由表分为160个K桶,上面的那个例子中,由于node a和node b距离是2^3,因此node b会存放在node a的第三个bucket。这里稍微解释下,这是由于node的id是160位,全部的id范围在0~2^160,一个路由160个bucket恰好覆盖id的全部的范围。那么这样是否是意味着数字越大的桶里面的节点数也就越多呢?为了防止一个bucket里面节点太多,因此规定了每一个bucket最多有8个节点,那么当有别的节点来了,又超过了8个节点的时候应该怎么办呢?这个问题放到后面再来说。</font>spa

那怎么来创建路由表呢?首先dht网络中有4个方法,经过这四个方法能够来创建路由表游戏

  • ping 检查一个节点是否在线ip

  • find_node 查找一个节点

  • get_peers 查找指定的某个infohash

  • announce_peer 发起一个通知,用来告诉节点下载完了

<font color=red >创建路由表,最重要的就是find_node,每发起一次find_node请求,对方就会返回距离被查询节点最近的前8个节点,经过不断的find_node,咱们的路由表就创建了。 好了,到了这里其实还会有不少疑问?比方说路由表有什么用,创建了路由表后又怎么获取infohash呢?仍是没讲明白,带着这几个问题继续下面的分析。</font>

假设有一个infohash xxx,那么怎么知道xxx在哪一个节点上面呢?这个地方很关键,由于infohash也是160bit,因此能够用一样的方法进行异或计算。由于在dht中规定离infohash距离最近的N个节点有责任知道这个infohash在哪,可是不必定保存这个infohash。这里用到的是get_peers,经过get_peers能够查找一个infohash,具体流程以下

(1) 从路由表中查到最近的8个node,依次发起get_peers请求 (2)若是没有查到的状况下,那么会返回离对方最近的8个node,继续对返回的node进行get_peers请求 (3)若是查到了,那么就返回values参数,里面包含了 拥有该infohash的ip和port

从上面能够知道,若是没有查到infohash对应的node,那么会不断从路由表最近的节点里面去查找,固然最后可能会找不到。 还有个announce_peer没说,上面提到dht中规定离infohash距离最近的N个节点有责任知道这个infohash在哪,当你从某个节点获取到infohash的时候,就要告诉那些节点你也获取到了infohash,这样那些节点就会保存你也有infohash这个信息。 好了只剩下ping这个没说了,ping主要用来检测一个节点是否存在存活。上面有说到路由表的每一个bucket只能存放最多8个节点,当有新的节点来的时候,又超过了8个节点。这个时候就会分红两个状况。

  • 该节点原本所在的bucket不是该bucket,那么bucket就会一分为二,由于一开始bucket只有一个,而不是一开始就有160个(后面讲实现还会再来说)
  • 该节点所在的bucket已经存在了8个节点,那么就会对节点发起ping,会替换没有响应的请求,若是全部节点都是好的,那么就丢弃该节点。

上面说的那四个ping,find_node,get_peers,announce_peer中的任意一个 当客户端收到请求的时候就会把他们加入本身的路由表。

<font color=red >好了讲了这么多,如今来总结一下ping和find_node都是和路由表最相关的ping用来检查bucket中的节点状态,find_node用来构建路由表。get_peers和announce_peer都是和infohash最相关get_peers能够经过主动查找的方式获取infohash,announce_peer经过被动接受的方式获取到infohash。因此get_peers和announce_peer都是咱们从dht网络中获取infohash的最重要的方式。后面具体实现部分还会详细介绍。</font>

本人能力有限,若是有出入的地方请不啬指出,也但愿能留言和我讨论。 </font>

相关文章
相关标签/搜索