如何用java实现一个p2p种子搜索(4)-种子获取
种子获取
在上一篇中我们已经可以获取到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)-种子获取的更多相关文章
- 如何用java实现一个p2p种子搜索(1)-概念
前言 说句大实话,网上介绍怎么用java实现p2p种子的搜索这种资料不是特别多,大部分都是python的,用python的话就会简单很多,它里面有很多简单方便的包,libtorrent等等,当然你用这 ...
- 如何用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位的,因此下 ...
- 如何用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开发 ...
- Qunar机票技术部就有一个全年很关键的一个指标:搜索缓存命中率,当时已经做到了>99.7%。再往后,每提高0.1%,优化难度成指数级增长了。哪怕是千分之一,也直接影响用户体验,影响每天上万张机票的销售额。 在高并发场景下,提供了保证线程安全的对象、方法。比如经典的ConcurrentHashMap,它比起HashMap,有更小粒度的锁,并发读写性能更好。线程安全的StringBuilder取代S
Qunar机票技术部就有一个全年很关键的一个指标:搜索缓存命中率,当时已经做到了>99.7%.再往后,每提高0.1%,优化难度成指数级增长了.哪怕是千分之一,也直接影响用户体验,影响每天上万张机 ...
随机推荐
- CRM公海自动回收规则
企微云CRM操作指南 – 道一云|企微https://wbg.do1.com.cn/xueyuan/2568.html 销售云 - 美洽 - 连接客户,亲密无间https://meiqia.com/s ...
- SVN和Git 介绍,区别,优缺点以及适用范围
SVN是Subversion的简称,是一个开放源代码的版本控制系统,支持大多数常见的操作系统.作为一个开源的版本控制系统,Subversion管理着随时间改变的数据.这些数据放置在一个中央资料档案库( ...
- Python进阶7--正则表达式
正则表达式*** 概述 分类 基本语法 元字符 ^ 匹配字符串的开头 $ 匹配字符串的末尾. . 匹配任意字符,除了换行符,当re.DOTALL标记被指定时,则可以匹配包括换行符的任意字符. [.. ...
- Vue.js 2.x笔记:服务请求axios(8)
1. axios简介 vue2.0之后,推荐使用axios. axios官方地址:https://github.com/axios/axios 2. axios安装 npm安装: npm instal ...
- Spring Boot 2.x 编写 RESTful API (二) 校验
用Spring Boot编写RESTful API 学习笔记 约束规则对子类依旧有效 groups 参数 每个约束用注解都有一个 groups 参数 可接收多个 class 类型 (必须是接口) 不声 ...
- bugku web web5
JSPFUCK??????答案格式CTF{**} http://123.206.87.240:8002/web5/ 字母大写 jspfuck这不是骂人吗,怎么回事啊? ·点进去看见有一个可以输入的框, ...
- docker报错:Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)
在github上开到这样一条 于是 这两个选项换着来 具体怎么回事,咱也不知道,咱也不敢问 改完后蹭蹭的
- centos7使用kubeadm安装部署kubernetes 1.14
应用背景: 截止目前为止,高热度的kubernetes版本已经发布至1.14,在此记录一下安装部署步骤和过程中的问题排查. 部署k8s一般两种方式:kubeadm(官方称目前已经GA,可以在生产环境使 ...
- CF271D 【Good Substrings】
定义哈希函数 \(H(c)=\sum_{i = 1} ^ m c_i*b^{m-i}\) \(H(C,K+1)=H(C,K)*b+C_{K+1}\)(K个坏字母) 用long long #includ ...
- luogu5283 异或粽子
题目链接 思路 首先求个前缀异或和,这样就可以\(O(1)\)的得到区间异或和了. 然后发现问题转化为 找出不同的\(k\)个二元组\(x,y\).使得\(a_x \otimes a_y\)的和最大. ...