如何用java實現一個p2p種子搜索(1)-概念


前言

說句大實話,網上介紹怎么用java實現p2p種子的搜索這種資料不是特別多,大部分都是python的,用python的話就會簡單很多,它里面有很多簡單方便的包,libtorrent等等,當然你用這些包可以實現功能,但是它封裝了太好,以致於你很難知道里面的細節。為了深入了解,然后我就用java實現了一把,當然中間遇到了很多的問題,也參考了github的項目。

說到p2p,我想大家可能都用種子下載過文件,比較常見的就是.torrent結尾的文件,通過種子可以下載到種子對應的文件。

基本概念

那么什么是種子呢?種子其實就是一個文件里面保存的是一個字典,其中最重要的一個字段就是info字段,里面保存文件名和文件長度。還有一個比較重要的字段是announce,這個是tracker地址通過這個地址可以查找到這個文件在哪些peer上面。還有一些種子沒有announce字段,這樣的種子被稱為trackerless torrent,會有nodes字段來代替。

除了種子可以下載文件,還有一種磁力鏈接也可以下載文件。那么什么是磁力鏈接呢?just like magnet:?xt=urn:btih:19838A8C4DE7DC2E34382249C9A52CFD9E3BB41A

復制這個鏈接,打開迅雷會讓你下載權利的游戲,磁力鏈接的前面部分是不變的,也就是說最后那一長串字符串才是對應了你下載的資源。最后一長串的字符串叫做infohash,每個種子都會對應一個infohash,所以磁力鏈接就是根據infohash來下載種子對應的文件。所以我們只要收集到足夠多的infohash,然后根據infohash再找到那些文件,建立起infohash和文件的對應關系,那么我們的搜索也就完成了,這里其實做的是磁力的搜索。

好了現在我們需要解決兩個問題,第一個是怎么爬取infohash,第二個是怎么通過infohash來找到種子包含的文件名。

那么我們怎么能夠獲取到infohash呢?這里以bittorrnt dht protocol為例

要想獲取infohash就必須成為dht網絡中的一員,dht網絡中由很多node組成,每個node由160個bit組成。每個node呢,都有存儲其中一部分node的信息,這個信息呢包括node的id,ip,port等等,在存儲node id的時候也不是隨便存儲,會按照距離的遠近來存儲,這里的距離不是物理的距離 ,而是邏輯距離。通過兩個id的異或來計算。舉個小例子 node a 的id是1110, node b的id等於0110,那么node a和node b之間的距離就是2的3次方。node的信息都存在路由表里面,每個路由表分為160個K桶,上面的那個例子中,因為node a和node b距離是2^3,所以node b會存放在node a的第三個bucket。這里稍微解釋下,這是因為node的id是160位,所有的id范圍在0~2^160,一個路由160個bucket剛好覆蓋id的所有的范圍。那么這樣是不是意味着數字越大的桶里面的節點數也就越多呢?為了防止一個bucket里面節點太多,所以規定了每個bucket最多有8個節點,那么當有別的節點來了,又超過了8個節點的時候應該怎么辦呢?這個問題放到后面再來講。

那怎么來建立路由表呢?首先dht網絡中有4個方法,通過這四個方法可以來建立路由表

  • ping 檢查一個節點是否在線

  • find_node 查找一個節點

  • get_peers 查找指定的某個infohash

  • announce_peer 發起一個通知,用來告訴節點下載完了

建立路由表,最重要的就是find_node,每發起一次find_node請求,對方就會返回距離被查詢節點最近的前8個節點,通過不斷的find_node,我們的路由表就建立了。
好了,到了這里其實還會有很多疑問?比方說路由表有什么用,建立了路由表后又怎么獲取infohash呢?還是沒講明白,帶着這幾個問題繼續下面的分析。

假設有一個infohash xxx,那么怎么知道xxx在哪個節點上面呢?這個地方很關鍵,因為infohash也是160bit,所以可以用同樣的方法進行異或計算。因為在dht中規定離infohash距離最近的N個節點有責任知道這個infohash在哪,但是不一定保存這個infohash。這里用到的是get_peers,通過get_peers可以查找一個infohash,具體流程如下

(1) 從路由表中查到最近的8個node,依次發起get_peers請求
(2)如果沒有查到的情況下,那么會返回離對方最近的8個node,繼續對返回的node進行get_peers請求
(3)如果查到了,那么就返回values參數,里面包含了 擁有該infohash的ip和port

從上面可以知道,如果沒有查到infohash對應的node,那么會不斷從路由表最近的節點里面去查找,當然最后可能會找不到。
還有個announce_peer沒說,上面提到dht中規定離infohash距離最近的N個節點有責任知道這個infohash在哪,當你從某個節點獲取到infohash的時候,就要告訴那些節點你也獲取到了infohash,這樣那些節點就會保存你也有infohash這個信息。
好了只剩下ping這個沒說了,ping主要用來檢測一個節點是否存在存活。上面有說到路由表的每個bucket只能存放最多8個節點,當有新的節點來的時候,又超過了8個節點。這個時候就會分成兩個情況。

  • 該節點本來所在的bucket不是該bucket,那么bucket就會一分為二,因為一開始bucket只有一個,而不是一開始就有160個(后面講實現還會再來講)
  • 該節點所在的bucket已經存在了8個節點,那么就會對節點發起ping,會替換沒有響應的請求,如果所有節點都是好的,那么就丟棄該節點。

上面說的那四個ping,find_node,get_peers,announce_peer中的任意一個 當客戶端收到請求的時候就會把他們加入自己的路由表。

好了講了這么多,現在來總結一下ping和find_node都是和路由表最相關的ping用來檢查bucket中的節點狀態,find_node用來構建路由表。get_peers和announce_peer都是和infohash最相關get_peers可以通過主動查找的方式獲取infohash,announce_peer通過被動接受的方式獲取到infohash。所以get_peers和announce_peer都是我們從dht網絡中獲取infohash的最重要的方式。后面具體實現部分還會詳細介紹。

本人能力有限,如果有出入的地方請不嗇指出,也希望能留言和我討論。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM