种子获取

在上一篇中我们已经可以获取到dht网络中的infohash了,所以我们只需要通过infohash来获取到种子,最后获取种子里面的文件名,然后和获取到的infohash建立对应关系,那么我们的搜索的数据就算落地了,有了数据再把数据导到es,搜索就算完成了。

获取种子我们需要和其他的peer交互,所以需要使用peer wire protocal发送握手数据包,握手数据包是68字节,第一个字节必须是19代表长度,后面是协议固定为BitTorrent protocol刚好19个字节,然后再跟着8个保留字节。现在一共是28字节,最后40字节分别是infohash和nodeid这样合起来刚好是68字节

@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
byte[] infoHash = DHTUtil.hexStr2Bytes(this.infoHash);
byte[] sendBytes = new byte[68];
System.arraycopy(HANDSHAKE_BYTES, 0, sendBytes, 0, 28);
System.arraycopy(infoHash, 0, sendBytes, 28, 20);
System.arraycopy(routingTable.getNodeId(), 0, sendBytes, 48, 20);
ctx.channel().writeAndFlush(Unpooled.copiedBuffer(sendBytes));
}

在握手协议后呢还需要在发送一个握手协议,这是因为不是所有的peer都支持种子的下载,种子的下载使用的是扩展bep_0009协议。

这个握手协议发送一个参数为m的字典,格式如下:前面4字节是长度字段,后面1字节是message id用来确认消息,紧接着一个字节0代表握手,在后面就是m参数的那个字典实际的数据了,官方介绍是这样的

This message is sent as any other bittorrent message, with a 4 byte length prefix and a single byte identifying the message (the single byte being 20 in this case). At the start of the payload of the message, is a single byte message identifier. This identifier can refer to different extension messages and only one ID is specified, 0. If the ID is 0, the message is a handshake message which is described below. The layout of a general extended message follows (including the message headers used by the bittorrent protocol):
uint32_t length prefix. Specifies the number of bytes for the entire message. (Big endian)
uint8_t bittorrent message ID, = 20
uint8_t extended message ID. 0 = handshake, >0 = extended message as specified by the handshake.

具体发送代码:

public void sendHandshakeMsg(ChannelHandlerContext ctx) throws Exception{
Map<String, Object> extendMessageMap = new LinkedHashMap<>();
Map<String, Object> extendMessageMMap = new LinkedHashMap<>();
extendMessageMMap.put("ut_metadata", 1);
extendMessageMap.put("m", extendMessageMMap);
byte[] tempExtendBytes = bencode.encode(extendMessageMap);
byte[] extendMessageBytes = new byte[tempExtendBytes.length + 6];
extendMessageBytes[4] = 20;
extendMessageBytes[5] = 0;
byte[] lenBytes = DHTUtil.int2Bytes(tempExtendBytes.length + 2);
System.arraycopy(lenBytes, 0, extendMessageBytes, 0, 4);
System.arraycopy(tempExtendBytes, 0, extendMessageBytes, 6, tempExtendBytes.length);
ctx.channel().writeAndFlush(Unpooled.copiedBuffer(extendMessageBytes));
}

如果返回的消息里面包含ut_metadata和metadata_size,那么说明就支持种子下载协议,metadata_size代表种子的大小,因为每次下载最多是16Kb,所以我们需要根据返回的metadata_size进行分块下载。其中有两个参数一个是msg_type 具体的值有0 1 2,0 代表request也就是发起请求,1 代表data也就是数据,2 reject代表拒绝,还有一个参数是piece代表需要下载第几块数据。看起来还是挺简单的

@SneakyThrows
private void sendMetadataRequest(ChannelHandlerContext ctx, String s){
int ut_metadata= Integer.parseInt(s.substring(s.indexOf("ut_metadatai") + 12, s.indexOf("ut_metadatai") + 13));
String str=s.substring(s.indexOf("metadata_sizei") + 14, s.length());
int metadata_size=Integer.parseInt(str.substring(0, str.indexOf("e")));
//分块数
int blockSize = (int) Math.ceil((double) metadata_size / (16 << 10));
bs=blockSize;
log.info("blocksize="+blockSize);
//发送metadata请求
for (int i = 0; i < blockSize; i++) {
Map<String, Object> metadataRequestMap = new LinkedHashMap<>();
metadataRequestMap.put("msg_type", 0);
metadataRequestMap.put("piece", i);
byte[] metadataRequestMapBytes = bencode.encode(metadataRequestMap);
byte[] metadataRequestBytes = new byte[metadataRequestMapBytes.length + 6];
metadataRequestBytes[4] = 20;
metadataRequestBytes[5] = (byte) ut_metadata;
byte[] lenBytes = DHTUtil.int2Bytes(metadataRequestMapBytes.length + 2);
System.arraycopy(lenBytes, 0, metadataRequestBytes, 0, 4);
System.arraycopy(metadataRequestMapBytes, 0, metadataRequestBytes, 6, metadataRequestMapBytes.length);
ctx.channel().writeAndFlush(Unpooled.copiedBuffer(metadataRequestBytes));
}
}

发送完后,对返回结果进行解码,可以看到里面包含了种子的文件名,种子的长度等等。最后对解析到的文件名和infohash保存的到数据库和es

好了,到此我们介绍完了种子搜索的整个思路和实现,那其实在dht网络中获取到infohash,然后再下载种子,最后能成功的概率没有很高,我自己运行了好几天,数据量不太,infohash到是还算多,但是很多都不支持metadata下载,这个是最骚的。不过还可以根据一些现有的磁力种子网站根据http协议去解析,这样通过多种途径收集数据才算多。

在实现的过程中,遇到了很多问题,看了很多文档和资料,最终能实现感觉还是有点东西的,当然也参考了github上面种子搜索java实现,很多代码都是copy的,哈哈哈哈。

最后再贴一下源码地址吧https://github.com/mistletoe9527/dht-spider

如何用java实现一个p2p种子搜索(4)-种子获取的更多相关文章

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

    前言 说句大实话,网上介绍怎么用java实现p2p种子的搜索这种资料不是特别多,大部分都是python的,用python的话就会简单很多,它里面有很多简单方便的包,libtorrent等等,当然你用这 ...

  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. 如何用Java编写一段代码引发内存泄露

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

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

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

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

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

  9. Qunar机票技术部就有一个全年很关键的一个指标:搜索缓存命中率,当时已经做到了>99.7%。再往后,每提高0.1%,优化难度成指数级增长了。哪怕是千分之一,也直接影响用户体验,影响每天上万张机票的销售额。 在高并发场景下,提供了保证线程安全的对象、方法。比如经典的ConcurrentHashMap,它比起HashMap,有更小粒度的锁,并发读写性能更好。线程安全的StringBuilder取代S

    Qunar机票技术部就有一个全年很关键的一个指标:搜索缓存命中率,当时已经做到了>99.7%.再往后,每提高0.1%,优化难度成指数级增长了.哪怕是千分之一,也直接影响用户体验,影响每天上万张机 ...

随机推荐

  1. 一入OI深似海 4 —— 纪念我最后一次PJ(中)

    不知道怎么回事,直到比赛前10分钟才放我们进考场. 考场在体育馆里面,很大很壮观. 我匆匆忙忙地找到位子,屁股还没坐热,被老师告知不能带水. what?! 于是我只好把水放在统一放私人物品的地方. 电 ...

  2. Nginx 在线新增模块

    系统:Centos7.5 Nginx版本:1.12.2 今天给项目添加ssl证书时,发现nginx 竟然不支持ssl,经过查看,询问相关人员发现nginx编译的时候没有任何模块(历史原因).哎.... ...

  3. java订单金额分级计算

    package ord; import java.util.ArrayList; public class order { public String orderid; public user use ...

  4. canvas学习笔记,实用知识点总结(上)

    本博客是本人日常学习笔记,作为重要知识点的总结记录,随笔风格可能更倾向于个人的学习习惯和方式,若对您有帮助十分荣幸,若存在问题欢迎互相学习探讨,前端小白一枚在此恭候. 一.基本使用规则 1.创建画布 ...

  5. 在ConoHa上Centos7环境下源码安装部署LNMP

    本文记录了从源码,在Centos 7上手动部署LNMP环境的过程,为了方便以后对nginx和mariadb进行升级,这里采用yum的方式进行安装. 1.建立运行网站和数据库的用户和组 groupadd ...

  6. iOS presentViewController 方法引起的问题

    有个需求,在项目中随时使用 presentViewController来显示一个界面,比如弹窗提示或者人脸解锁,都是在任何情况都可能出现的. 在presentViewController 调用前,已经 ...

  7. [ffmpeg] 滤波

    ffmpeg中有很多已经实现好的滤波器,这些滤波器的实现位于libavfilter目录之下,用户需要进行滤波时,就是是调用这些滤波器来实现的.ffmpeg对于调用滤波器有一整套的调用机制. 基本结构 ...

  8. How to Change Error Message Colors in Windows 10 PowerShell Console

    While this was a really easy way to change some of the settings, what if you want to do more extensi ...

  9. content-type 组件

    content-type初识 什么是content-type ContentType是Django的内置的一个应用,可以追踪项目中所有的APP和model的对应关系,并记录在ContentType表中 ...

  10. Codeforces Round #554 (Div. 2) C. Neko does Maths(数学+GCD)

    传送门 题意: 给出两个整数a,b: 求解使得LCM(a+k,b+k)最小的k,如果有多个k使得LCM()最小,输出最小的k: 思路: 刚开始推了好半天公式,一顿xjb乱操作: 后来,看了一下题解,看 ...