前言

说句大实话,网上介绍怎么用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. mysql提取.sql备份文件中的单个表以及表数据

    背景:随着业务模块的不断在增多,数据库mysql容量也是越来越大,做测试时,整个备份还原比较耗费时间,由于有时候仅仅需要单个表或者少数几个表,要想从整个备份文件中提取指定的表以及数据,需要以下方法. ...

  2. jquery中关键字写错导致的错误——dataType写成dateType(data写成date)

    由于不会报错,会导致原本servlet后端传回的json字符串不能被正确解析为json格式,而只是显示为字符串. 具体错误表现为:在浏览器Console中显示为字符串,但是在json.cn中可以被正常 ...

  3. 五、Redis持久化配置

    转载:[https://www.cnblogs.com/xingzc/p/5988080.html] Redis提供的持久化机制(RDB和AOF) Redis提供的持久化机制 Redis是一种面向“k ...

  4. centos7之zabbix服务器的常规优化

    一.硬件需求分析 1.首先我们来分析一个硬件需求,这里我以400个agent计算,CPU建议是4核,内存不要少于8GB,硬盘只要不是用了很久的主机就行,容量的话建议300GB基本就够使用好一段时间了, ...

  5. grep -v 反向查找

    grep 是查找含有指定文本行 grep -v 是方向查找,比如 grep -v grep 就是查找 不含有 grep 内容的行,简单来说,就是过滤输入的 grep 命令 Demo: ps -aux ...

  6. React笔记:快速构建脚手架(1)

    1. Create React APP React官方提供的脚手架工程Create React App:https://github.com/facebook/create-react-app Cre ...

  7. ueditor 回显html样式会直接出来,无效

    百度了好久,后面发现可以直接这样

  8. ionic3创建选项卡

    html页面 <ion-content padding> <ion-segment [(ngModel)]="tabs"> <ion-segment- ...

  9. Forethought Future Cup - Elimination Round

    A:签到. #include<bits/stdc++.h> using namespace std; #define ll long long char getc(){char c=get ...

  10. ZOJ 3949 Edge to the Root( 树形dp)

    http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3949 题解:树dp真的很直觉,或者说dp真的很直觉.就上周末比赛时其实前一 ...