在上一篇中咱们已经能够获取到dht网络中的infohash了,因此咱们只须要经过infohash来获取到种子,最后获取种子里面的文件名,而后和获取到的infohash创建对应关系,那么咱们的搜索的数据就算落地了,有了数据再把数据导到es,搜索就算完成了。 获取种子咱们须要和其余的peer交互,因此须要使用peer wire protocal发送握手数据包,握手数据包是68字节,第一个字节必须是19表明长度,后面是协议固定为BitTorrent protocol恰好19个字节,而后再跟着8个保留字节。如今一共是28字节,最后40字节分别是infohash和nodeid这样合起来恰好是68字节java
@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参数的那个字典实际的数据了,官方介绍是这样的node
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.
具体发送代码:git
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表明须要下载第几块数据。看起来仍是挺简单的github
@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数据库