如何用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數據庫

相關文章
相關標籤/搜索