前言

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

说到p2p,我想大家可能都用种子下载过文件,比较常见的就是.torrent结尾的文件,通过种子可以下载到种子对应的文件。

基本概念

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

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

复制这个链接,打开迅雷会让你下载权利的游戏,磁力链接的前面部分是不变的,也就是说最后那一长串字符串才是对应了你下载的资源。最后一长串的字符串叫做infohash,每个种子都会对应一个infohash,所以磁力链接就是根据infohash来下载种子对应的文件。所以我们只要收集到足够多的infohash,然后根据infohash再找到那些文件,建立起infohash和文件的对应关系,那么我们的搜索也就完成了,这里其实做的是磁力的搜索。

好了现在我们需要解决两个问题,第一个是怎么爬取infohash,第二个是怎么通过infohash来找到种子包含的文件名。

那么我们怎么能够获取到infohash呢?这里以bittorrnt dht protocol为例

要想获取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个节点的时候应该怎么办呢?这个问题放到后面再来讲。

那怎么来建立路由表呢?首先dht网络中有4个方法,通过这四个方法可以来建立路由表

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

  • find_node 查找一个节点

  • get_peers 查找指定的某个infohash

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

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

假设有一个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中的任意一个 当客户端收到请求的时候就会把他们加入自己的路由表。

好了讲了这么多,现在来总结一下ping和find_node都是和路由表最相关的ping用来检查bucket中的节点状态,find_node用来构建路由表。get_peers和announce_peer都是和infohash最相关get_peers可以通过主动查找的方式获取infohash,announce_peer通过被动接受的方式获取到infohash。所以get_peers和announce_peer都是我们从dht网络中获取infohash的最重要的方式。后面具体实现部分还会详细介绍。

本人能力有限,如果有出入的地方请不啬指出,也希望能留言和我讨论。

如何用java实现一个p2p种子搜索(1)-概念的更多相关文章

  1. 如何用java实现一个p2p种子搜索(4)-种子获取

    种子获取 在上一篇中我们已经可以获取到dht网络中的infohash了,所以我们只需要通过infohash来获取到种子,最后获取种子里面的文件名,然后和获取到的infohash建立对应关系,那么我们的 ...

  2. 如何用java实现一个p2p种子搜索(3)-dht协议实现

    dht协议实现 上一篇完成了路由表的实现,建立了路由表后,我们还要对路由表进行初始化,因为一开始路由表为空,所以我们需要借助一些知名的dht网络中的节点,对这些节点进行find_node,然后一步步初 ...

  3. 如何用java实现一个p2p种子搜索(2)-路由表实现

    路由表实现 回顾一下上一篇讲的内容,上一篇提到从dht网络中获取infohash,那么加入dht网络后的最重要的第一步就是怎么去建立路由表. 路由表里面保存的是dht中其他node的信息,所以node ...

  4. 如何用java创建一个jdbc程序

    第一个jdbc程序 JDBC简介 Java数据库连接(Java Database Connectivity,JDBC),是一种用于执行SQL语句的Java API,它由一组用Java编程语言编写的类和 ...

  5. 如何用java完成一个中文词频统计程序

    要想完成一个中文词频统计功能,首先必须使用一个中文分词器,这里使用的是中科院的.下载地址是http://ictclas.nlpir.org/downloads,由于本人电脑系统是win32位的,因此下 ...

  6. 一个支持种子、磁力、迅雷下载和磁力搜索的APP源代码

    磁力搜索网站2020/01/12更新 https://www.cnblogs.com/cilisousuo/p/12099547.html 一个支持种子.磁力.迅雷下载和磁力搜索的APP源代码 Lic ...

  7. 如何用Java编写一段代码引发内存泄露

    本文来自StackOverflow问答网站的一个热门讨论:如何用Java编写一段会发生内存泄露的代码. Q:刚才我参加了面试,面试官问我如何写出会发生内存泄露的Java代码.这个问题我一点思路都没有, ...

  8. 五:用JAVA写一个阿里云VPC Open API调用程序

    用JAVA写一个阿里云VPC Open API调用程序 摘要:用JAVA拼出来Open API的URL 引言 VPC提供了丰富的API接口,让网络工程是可以通过API调用的方式管理网络资源.用程序和软 ...

  9. 基于python的种子搜索网站-开发过程

    本讲会对种子搜索网站的开发过程进行详细的讲解. 源码地址:https://github.com/geeeeeeeek/bt 项目开发过程 项目简介 该项目是基于python的web类库django开发 ...

随机推荐

  1. 工具(2): 极简MarkDown排版介绍(How to)

    如何切换编辑器 切换博客园编辑器为MarkDown:MarkDown Editor 选择一个在线编辑和预览站点:StackEdit 如何排版章节 MarkDown: 大标题 ========== 小标 ...

  2. 如何在Linux中轻松删除源安装的软件包?

    第1步:安装Stow 在这个例子中,我们使用的是CentOS,因此我们需要扩展的EPEL库.您可以使用以下命令安装它们:yum install epel-release然后,下面这段命令:yum in ...

  3. Netty 中 LengthFieldBasedFrameDecoder 构造函数取值备忘

    public LengthFieldBasedFrameDecoder(ByteOrder byteOrder, int maxFrameLength, int lengthFieldOffset, ...

  4. Docker常用镜像

    Docker,具有快捷方便的特性,机器上不需要安装软件和进行各种配置,拉取镜像,一行命令即可启动服务,不使用时,一行命令关闭容器即可,快捷方便,干净.利索.建议将本地的redis.mysql.kafk ...

  5. 【linux】线上服务器要关注哪些参数

    服务器(nginx/apache): 1.吞吐率. 2.并发连接数. 3.qps. 4.并发连接数详细数据统计,包括读取请求.持久连接.发送响应内容.关闭连接.等待连接. 5.连接线程池利用率. 关系 ...

  6. antd Tree组件中,自定义右键菜单

    最近项目中,有一个需求是自定义antd的Tree组件的右键菜单功能. 直接上代码 class Demo extends Component { state = { rightClickNodeTree ...

  7. MVN TEST指定运行脚本

    clean:表示将你上一次编译生成的一些文件删除 test:表示只执行测试代码 >mvn clean test -Dtest=[ClassName] 运行测试类中指定的方法:这个需要maven- ...

  8. mybatis if test 判断字符串的坑

    今天调试一个非常简单的test判断字符串查询语句,怎么调试都是不好用,后来百度才发现,是我写的test标签写错了,我写成: <if test="record.current != nu ...

  9. 码云报错:fatal: remote origin already exists.解决方法

    今天在提交Git的时候,遇到了几个问题,记录一下,方便以后查找O(∩_∩)O 第一个问题 git remote add origin************** fatal: remote origi ...

  10. (二叉树 递归) leetcode 889. Construct Binary Tree from Preorder and Postorder Traversal

    Return any binary tree that matches the given preorder and postorder traversals. Values in the trave ...