<font size=2>說句大實話,網上介紹怎麼用java實現p2p種子的搜索這種資料不是特別多,大部分都是python的,用python的話就會簡單不少,它裏面有不少簡單方便的包,libtorrent等等,固然你用這些包能夠實現功能,可是它封裝了太好,以至於你很難知道里面的細節。爲了深刻了解,而後我就用java實現了一把,固然中間遇到了不少的問題,也參考了github的項目。java
說到p2p,我想你們可能都用種子下載過文件,比較常見的就是.torrent結尾的文件,經過種子能夠下載到種子對應的文件。node
那麼什麼是種子呢?種子其實就是一個文件裏面保存的是一個字典,其中最重要的一個字段就是info字段,裏面保存文件名和文件長度。還有一個比較重要的字段是announce,這個是tracker地址經過這個地址能夠查找到這個文件在哪些peer上面。還有一些種子沒有announce字段,這樣的種子被稱爲trackerless torrent,會有nodes字段來代替。python
除了種子能夠下載文件,還有一種磁力連接也能夠下載文件。那麼什麼是磁力連接呢?just like magnet:?xt=urn:btih:19838A8C4DE7DC2E34382249C9A52CFD9E3BB41Agit
複製這個連接,打開迅雷會讓你下載權利的遊戲,磁力連接的前面部分是不變的,也就是說最後那一長串字符串纔是對應了你下載的資源。<font color=red >最後一長串的字符串叫作infohash,每一個種子都會對應一個infohash,因此磁力連接就是根據infohash來下載種子對應的文件。因此咱們只要收集到足夠多的infohash,而後根據infohash再找到那些文件,創建起infohash和文件的對應關係,那麼咱們的搜索也就完成了,這裏其實作的是磁力的搜索。</font>github
好了如今咱們須要解決兩個問題,第一個是怎麼爬取infohash,第二個是怎麼經過infohash來找到種子包含的文件名。網絡
那麼咱們怎麼可以獲取到infohash呢?這裏以bittorrnt dht protocol爲例less
<font color=red >要想獲取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個節點的時候應該怎麼辦呢?這個問題放到後面再來說。</font>spa
那怎麼來創建路由表呢?首先dht網絡中有4個方法,經過這四個方法能夠來創建路由表遊戲
ping 檢查一個節點是否在線ip
find_node 查找一個節點
get_peers 查找指定的某個infohash
announce_peer 發起一個通知,用來告訴節點下載完了
<font color=red >創建路由表,最重要的就是find_node,每發起一次find_node請求,對方就會返回距離被查詢節點最近的前8個節點,經過不斷的find_node,咱們的路由表就創建了。 好了,到了這裏其實還會有不少疑問?比方說路由表有什麼用,創建了路由表後又怎麼獲取infohash呢?仍是沒講明白,帶着這幾個問題繼續下面的分析。</font>
假設有一個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個節點。這個時候就會分紅兩個狀況。
上面說的那四個ping,find_node,get_peers,announce_peer中的任意一個 當客戶端收到請求的時候就會把他們加入本身的路由表。
<font color=red >好了講了這麼多,如今來總結一下ping和find_node都是和路由表最相關的ping用來檢查bucket中的節點狀態,find_node用來構建路由表。get_peers和announce_peer都是和infohash最相關get_peers能夠經過主動查找的方式獲取infohash,announce_peer經過被動接受的方式獲取到infohash。因此get_peers和announce_peer都是咱們從dht網絡中獲取infohash的最重要的方式。後面具體實現部分還會詳細介紹。</font>
本人能力有限,若是有出入的地方請不嗇指出,也但願能留言和我討論。 </font>