种子获取

在上一篇中我们已经可以获取到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. 滴滴 CTO 架构师 业务 技术 战役 时间 赛跑 超前 设计

    滴滴打车CTO张博:生死战役,技术和时间赛跑-CSDN.NEThttps://www.csdn.net/article/2015-06-25/2825058-didi 滴滴出行首席架构师李令辉:业务的 ...

  2. mysql原生语句基础知识

    要操作数据库,首先要登录mysql: *mysql -u root -p 密码 创建数据库: *create database Runoob(数据库名); 删除数据库: *drop database ...

  3. NLP语义匹配

    参考资料 [搜狗语义匹配技术前沿]https://www.jiqizhixin.com/articles/2018-10-25-16?from=synced&keyword=%E6%90%9C ...

  4. C# call webservice方法

    https://www.cnblogs.com/Fooo/p/5507153.html

  5. cocos 常用组件

    前面的话 本文将详细介绍 cocos 中的常用组件 Sprite [概述] Sprite(精灵)是 2D 游戏中最常见的显示图像的方式,在节点上添加 Sprite 组件,就可以在场景中显示项目资源中的 ...

  6. 【CERC2016】【BZOJ4792】村庄 搜索

    题目大意 有一个 \(2^n\times 2^n\) 的网格,左下角坐标为 \((0,0)\),右上角坐标为 \((2^n,2^n)\). 定义格点 \((x,y)\) 为坐标系中坐标为 \((x,y ...

  7. htaccess 的使用基本小节 For apache httpd

    htaccess 的使用基本小节 For apache httpd .htaccess的基本作用 .htaccess是一个纯文本文件,它里面存放着Apache服务器配置相关的指令.       .ht ...

  8. MT【330】u,v,w法

    已知$a^2+b^2+c^2=1$求$abc(a+b+c)$的最小值.(2018辽宁预赛解答压轴题) 不妨设$a+b+c=3u,ab+bc+ca=3v^2,abc=w^3$,令$u^2=tv^2$要求 ...

  9. [File transfer]Syncthing

    https://syncthing.net/ 另外两种1.filezila 2.python -m http

  10. Redis主从配置和哨兵监控配置——服务器端环境搭建

    一:介绍 公司用到的redis框架,主要分为cluster的缓存集群和sentinel中的哨兵主从.这种的选用方式一般需要更具业务场景来做区分,两种框架的配置图为:右图为哨兵主从框架和cliuster ...