如何用java实现一个p2p种子搜索(4)-种子获取

种子获取

在上一篇中咱们已经能够获取到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数据库

相关文章
相关标签/搜索