在上一篇中咱們已經能夠獲取到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數據庫