种子获取

在上一篇中我们已经可以获取到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. P1119 灾后重建(floyd进阶)

    思路:这道题看n的范围很小(n<=200),显然就用floyd可以解决的问题,但又并不是简单的floyd算法,还是需要一些小小的变化.一开始我的思路是先跑一次弗洛伊德最短路,这样子显然复杂度很高 ...

  2. [Cordova inAppBrowser 在App内打开浏览器]

    方案1: 使用Cordova插件 cordova-plugin-inappbrowser 1.  添加插件 cordova plugin add cordova-plugin-inappbrowser ...

  3. mysql-笔记-默认值

    1 指定列的默认值 columnName int default '-1' 2 integer 列-设置自增列 也是指定默认值的方式 3 默认值必须是常量 不能使用函数.表达式---特例:timest ...

  4. DS博客作业03--栈和队列

    1.本周学习总结 本周学习中学习了栈和队列,栈和队列都属于线性结构,栈和队列不同于线性表的地方在于它们的相关运算具有一些特殊性,所以栈和队列也称为操作受限的线性表. 1.栈 栈是重要且常用的数据结构之 ...

  5. jvm调优参数

    堆溢出测试args:-verbose:gc -Xms20M -Xmx20M -XX:+PrintGCDetails 栈层级不足args:-Xss128k 常量池内存溢出args : -XX:PermS ...

  6. ADO.Net笔记整理(一)

    几次装机,Notes已烟消云散,近日因为Node.js死活搞不定,无奈装机,备份好的东东,没想到磁盘扇区出现异常,可能是PE启动盘的病毒,只好将磁盘全部重新分区,恢复数据也懒得恢复了,日积月累关乎将来 ...

  7. Linux haproxy基础

    代理作用 web缓存,提供缓存功能,可以加速响应过程. 反向代理,可以隐藏后端服务器 内容路由,可把不同内容类型的请求转发至特定服务器, 转码器,与客户端通信,由于带宽限制,可将报文转码压缩:与后端服 ...

  8. logstash配置多入多出并互相隔离

    需求:需要利用同一logstash进程采集不同日志,输出到es的不同index,各输入输出隔离: 主要需要解决如下两个问题: 1.如何加载多个配置文件? 普通启动方式:nohup bin/logsta ...

  9. python自动启动appium服务

    我们可以通过socket模块来检查appium是否启动,如果未启动,可以通过subprocess模块来启动,需要导入socket和subprocess模块,具体如下图: 第1个函数:表示检查appiu ...

  10. STL迭代器iterator

    一:迭代器原理 迭代器是一个“可遍历STL容器内全部或部分元素”的对象. 迭代器指出容器中的一个特定位置. 迭代器就如同一个指针. 迭代器提供对一个容器中的对象的访问方法,并且可以定义了容器中对象的范 ...