如何用java实现一个p2p种子搜索(1)-概念
前言
说句大实话,网上介绍怎么用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)-概念的更多相关文章
- 如何用java实现一个p2p种子搜索(4)-种子获取
种子获取 在上一篇中我们已经可以获取到dht网络中的infohash了,所以我们只需要通过infohash来获取到种子,最后获取种子里面的文件名,然后和获取到的infohash建立对应关系,那么我们的 ...
- 如何用java实现一个p2p种子搜索(3)-dht协议实现
dht协议实现 上一篇完成了路由表的实现,建立了路由表后,我们还要对路由表进行初始化,因为一开始路由表为空,所以我们需要借助一些知名的dht网络中的节点,对这些节点进行find_node,然后一步步初 ...
- 如何用java实现一个p2p种子搜索(2)-路由表实现
路由表实现 回顾一下上一篇讲的内容,上一篇提到从dht网络中获取infohash,那么加入dht网络后的最重要的第一步就是怎么去建立路由表. 路由表里面保存的是dht中其他node的信息,所以node ...
- 如何用java创建一个jdbc程序
第一个jdbc程序 JDBC简介 Java数据库连接(Java Database Connectivity,JDBC),是一种用于执行SQL语句的Java API,它由一组用Java编程语言编写的类和 ...
- 如何用java完成一个中文词频统计程序
要想完成一个中文词频统计功能,首先必须使用一个中文分词器,这里使用的是中科院的.下载地址是http://ictclas.nlpir.org/downloads,由于本人电脑系统是win32位的,因此下 ...
- 一个支持种子、磁力、迅雷下载和磁力搜索的APP源代码
磁力搜索网站2020/01/12更新 https://www.cnblogs.com/cilisousuo/p/12099547.html 一个支持种子.磁力.迅雷下载和磁力搜索的APP源代码 Lic ...
- 如何用Java编写一段代码引发内存泄露
本文来自StackOverflow问答网站的一个热门讨论:如何用Java编写一段会发生内存泄露的代码. Q:刚才我参加了面试,面试官问我如何写出会发生内存泄露的Java代码.这个问题我一点思路都没有, ...
- 五:用JAVA写一个阿里云VPC Open API调用程序
用JAVA写一个阿里云VPC Open API调用程序 摘要:用JAVA拼出来Open API的URL 引言 VPC提供了丰富的API接口,让网络工程是可以通过API调用的方式管理网络资源.用程序和软 ...
- 基于python的种子搜索网站-开发过程
本讲会对种子搜索网站的开发过程进行详细的讲解. 源码地址:https://github.com/geeeeeeeek/bt 项目开发过程 项目简介 该项目是基于python的web类库django开发 ...
随机推荐
- 工具(2): 极简MarkDown排版介绍(How to)
如何切换编辑器 切换博客园编辑器为MarkDown:MarkDown Editor 选择一个在线编辑和预览站点:StackEdit 如何排版章节 MarkDown: 大标题 ========== 小标 ...
- 如何在Linux中轻松删除源安装的软件包?
第1步:安装Stow 在这个例子中,我们使用的是CentOS,因此我们需要扩展的EPEL库.您可以使用以下命令安装它们:yum install epel-release然后,下面这段命令:yum in ...
- Netty 中 LengthFieldBasedFrameDecoder 构造函数取值备忘
public LengthFieldBasedFrameDecoder(ByteOrder byteOrder, int maxFrameLength, int lengthFieldOffset, ...
- Docker常用镜像
Docker,具有快捷方便的特性,机器上不需要安装软件和进行各种配置,拉取镜像,一行命令即可启动服务,不使用时,一行命令关闭容器即可,快捷方便,干净.利索.建议将本地的redis.mysql.kafk ...
- 【linux】线上服务器要关注哪些参数
服务器(nginx/apache): 1.吞吐率. 2.并发连接数. 3.qps. 4.并发连接数详细数据统计,包括读取请求.持久连接.发送响应内容.关闭连接.等待连接. 5.连接线程池利用率. 关系 ...
- antd Tree组件中,自定义右键菜单
最近项目中,有一个需求是自定义antd的Tree组件的右键菜单功能. 直接上代码 class Demo extends Component { state = { rightClickNodeTree ...
- MVN TEST指定运行脚本
clean:表示将你上一次编译生成的一些文件删除 test:表示只执行测试代码 >mvn clean test -Dtest=[ClassName] 运行测试类中指定的方法:这个需要maven- ...
- mybatis if test 判断字符串的坑
今天调试一个非常简单的test判断字符串查询语句,怎么调试都是不好用,后来百度才发现,是我写的test标签写错了,我写成: <if test="record.current != nu ...
- 码云报错:fatal: remote origin already exists.解决方法
今天在提交Git的时候,遇到了几个问题,记录一下,方便以后查找O(∩_∩)O 第一个问题 git remote add origin************** fatal: remote origi ...
- (二叉树 递归) 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 ...