[C#搜片神器] 之P2P中DHT網絡爬蟲原理

 

繼續接着上一篇寫:使用C#實現DHT磁力搜索的BT種子後端管理程序+數據庫設計(開源)[搜片神器]html

 

昨天因爲開源的時候沒有注意運行環境,直接沒有考慮下載BT種子文件時生成子文件夾,可能致使有的朋友運行沒有結果,在此表示對支持開源的朋友道謙.另外也對源程序增長了一些說明,已經提交.node

開源地址:https://github.com/h31h31/H31DHTMgrgit

程序下載:H31DHT下載github

我的電腦編譯環境是WIN7+VS2005,若是程序運行出錯,請自行下載代碼進行編譯.算法

先說下運行方法:數據庫

1)有固定IP的朋友能夠試試H31DHT.exe數據抓取程序,會獲取一些數據,若是>2小時尚未數據返回,直接說明不是固定IP的返回數據不多;後端

2)直接從http://torrage.com/sync下載幾個文本文件回來,放到程序目錄下,H31DHTMgr程序會自動遍歷這個文件夾取HASH文件,安全

存儲到數據庫中,若是將此網站的200萬數據(我的估計的)所有下載成功,那也能夠搜索不少內容了.服務器

3)新程序界面以下:須要的本身下載源代碼進行編譯(VS2005),不提供EXE下載,但願你們感興趣的一塊兒開源下;網絡

[增長了複製磁連接和下載選中項目的代碼,以前只能查看,不能顯示.]

--------------------先來點你們感興趣的東西-------------------------------------

 你們可能問目前的程序採用什麼方法下載BT種子的比較關心,下面就本身的體會給你們說說:

DHT磁力種子其實就是20字節的HASH值,這個值能夠直接從不少網站下載種子,舉例子說明:

好比說上一篇文件中有那麼多HASH值的字符串,怎麼利用呢,好比有個HASH值13ce77b3b934b12dc77fded6646426a6db5c3428,有40位,由於在內存裏面佔用20位,顯示爲16進制因此顯示爲40位了;

有這個HASH值,咱們能夠加上磁頭magnet:?xt=urn:btih:     兩個合在一塊兒就能夠下載BT種子了,

固然須要使用BT工具,(magnet:?xt=urn:btih:13ce77b3b934b12dc77fded6646426a6db5c3428)複製試下.

但咱們的程序沒有使用BT協議去下載,而是經過別人的網站下載.

好比http://torrage.com/torrent/13ce77b3b934b12dc77fded6646426a6db5c3428.torrent 你們分析組合方式就明白了,

會提示找不到這個種子,那就說明這個網站沒有收集到最新的BT種子.

能夠從其它網站下載,你們能夠去看下源程序裏面的組合方法.

 

 

-------------------------下面介紹一些從網上收集的資料信息----------------------------------------------

DHT網絡爬蟲基於DHT網絡構建了一個P2P資源搜索引擎。這個搜索引擎不但能夠用於構建DHT網絡中活躍的資源索引(活躍的資源意味着該網絡中確定有人至少持有該資源的部分數據),還能夠分析出該網絡中的熱門分享資源。網絡上其實也有其餘人作了相似的應用:DHTmonitoringCrawling Bittorrent DHT

DHT/Magnet/Torrent

在P2P網絡中,要經過種子文件下載一個資源,須要知道整個P2P網絡中有哪些計算機正在下載/上傳該資源。這裏將這些提供某個資源下載的計算機定義爲peer。傳統的P2P網絡中,存在一些tracker服務器,這些服務器的做用主要用於跟蹤某個資源有哪些關聯的peer。下載這個資源固然得首先取得這些peer。

DHT的出現用於解決當tracker服務器不可用時,P2P客戶端依然能夠取得某個資源的peer。DHT解決這個問題,是由於它將原來tracker上的資源peer信息分散到了整個網絡中。這裏將實現了DHT協議的計算機定義爲節點(node)。一般一個P2P客戶端程序既是peer也是節點。DHT網絡有多種實現算法,例如Kademlia。

當某個P2P客戶端經過種子文件下載資源時,若是沒有tracker服務器,它就會向DHT網絡查詢這個資源對應的peer列表。資源的標識在DHT網絡中稱爲infohash,是一個20字節長的字符串,通常經過sha1算法得到,也就是一個相似UUID的東西。

實際上,種子文件自己就對應着一個infohash,這個infohash是經過種子文件的文件描述信息動態計算獲得。一個種子文件包含了對應資源的描述信息,例如文件名、文件大小等。Magnet,這裏指的是磁力連接,它是一個相似URL的字符串地址。P2P軟件經過磁力連接,會下載到一個種子文件,而後根據該種子文件繼續真實資源的下載。

磁力連接中包含的最重要的信息就是infohash。這個infohash通常爲40字節或32字節,它其實只是資源infohash(20字節)的一種編碼形式。

 

Kademlia

各類DHT的實現算法,不管是Chord, Pastry仍是Kademlia,其最直接的目標就是以最快的速度來定位到指望的節點,在P2P文件分享應用中則是以最快的速度來查找到正在分享某一文件/種子的peers列表信息。由於每一個節點都是分佈式存在於地球的任何角落,若是用地理距離來衡量兩節點間的距離則可能給計算帶來極大複雜性甚至不可能進行衡量,所以基本全部的DHT算法都是採用某種邏輯上的距離,在Kademlia則採用簡單的異或計算來衡量兩節點間的距離,它和地理上的距離沒有任何關係,但卻具有幾何公式的絕大特徵:

(1)節點和它自己之間的異或距離是0

(2)異或距離是對稱的:即從A到B的異或距離與從B到A的異或距離是等同的

(3)異或距離符合三角形不等式:給定三個頂點A B C,假如AC之間的異或距離最大,那麼AC之間的異或距離必小於或等於AB異或距離和BC異或距離之和.

(4)對於給定的一個距離,距離A只存在有惟一的一個節點B,也即單向性,在查找路徑上也是單向的,這個和地理距離不一樣。

            Kademlia中規定全部的節點都具備一個節點ID,該節點ID的產生方法和種子文件中的info hash採用相同算法:即sha-1(安全hash算法),所以每一個節點的id,以及每一個共享文件/種子的info-hash都是惟一的,而且都是20個字符160bits位組成。兩個節點間的距離就是兩個節點id的異或結果,節點離鍵值(種子)的距離爲該節點的id和該種子文件的info-hash的異或結果。Kademlia在異或距離度量的基礎上又把整個DHT網絡拓撲組織成一個二叉前綴樹(XuanWu系統中arp的實現則是一個例子),全部的節點(全部的正在運行的,而且開取了DHT功能的Bt,Btspilits應用)等做爲該二叉前綴樹的葉子節點,能夠想象這棵二叉樹能夠容納多達2128個葉子(節點),這足以組織任何規模的網絡了。對於每一個節點來講按照離本身的遠近區域又能夠把這棵樹劃分爲160棵子樹,每個子樹和該節點都有一個共同的前綴,共同前綴越少離得越遠。以下圖所示:

(注意:上圖只是一個劃分子樹的例子,節點都沒有位於同一層的葉子上面)

以上圖紅色節點位例0011位例,它能夠把其餘的節點劃分位4棵不一樣子樹,離本身越近子樹和本身有越長的公共前綴,若是節點是均勻分佈則離本身越近的子樹含有的葉子節點更少(兄弟只有一個即和本身有159個共同前綴的那個)。由於節點都位於該樹最底層的葉子位置,水平看上去則全部的葉子都在一條線上,若是把這條線看成2128空間的每個點,則更能體現上面的劃分特性(折半拆分)。爲了能快速到達這160棵子樹,處於DHT網絡中的每個節點都記錄了每棵子樹上的k個節點的信息(ip,port,id),在BT中K固定爲8,好比上圖中紅色節點就可能保存有最左邊子樹的8個葉子節點信息,固然靠近本身的子樹可能沒有8個葉子,則把全部當前存在的葉子記錄上去,這份記錄信息在Kademlia算法中叫做K桶,也叫做「路由表」,固然這個「路由表」的信息和咱們IP路由的含義有點不一樣,它表明的是爲了到達處於距離本身某範圍[ 2i — 2i+1 )的節點,能夠經過該範圍內的選取的k個節點來進一步定位.

Kademlia是DHT網絡的一種實現。網絡上關於這個算法的文章,主要是圍繞整個DHT網絡的實現原理進行論述。我的以爲這些文章很蛋疼,基本上讀了以後對於要如何去實現一個DHT客戶端仍是沒有概念。這裏主要可參考P2P中DHT網絡介紹,以及BitTorrent網站上的DHT協議描述

Kad的主要目的是用於查詢某個資源對應的peer列表,而這個peer列表其實是分散在整個網絡中。網絡中節點數量很大,若是要得到peer列表,最簡單的作法無非就是依次詢問網絡中的每一個節點。這固然不可行。因此在Kad算法中,設立了一個路由表。每個節點都有一份路由表。這個是按照節點之間的距離關係構建出來的。節點之間的距離固然也有特定的算法定義,在Kad中經過對兩個節點的ID進行異或操做獲得。節點的ID和infohash經過相同算法構建,都是20字節長度。節點和infohash之間也有距離關係,實際上表示的是節點和資源的距離關係。

有了這個路由表以後,再經過一個基於距離關係的查找算法,就能夠實現不用挨個遍歷就找到特定的節點。而查找資源peer這個操做,正是基於節點查找這個過程。

路由表的實現,按個人理解,有點相似通常的hash表結構。在這個表中有160個桶,稱爲K桶,這個桶的數量在實現上能夠動態增加。每一個桶保存有限個元素,例如K取值爲8,指的就是這個桶最多保存8個元素。每一個元素就是一個節點,節點包含節點ID、地址信息以及peer信息。這些桶能夠經過距離值索引獲得,即距離值會通過一個hash算法,使其值落到桶的索引範圍內。

要加入一個DHT網絡,須要首先知道這個網絡中的任意一個節點。如何得到這個節點?在一些開源的P2P軟件中,會提供一些節點地址,例如transmission中提供的dht.transmissionbt.com:6881。

kademlia的消息:

爲了實現上面的「路由表」創建,刷新,獲取peers-list,保存peers-list這些功能,kademlia定義四個最基本的KRPC操做:

(1)ping操做,做用是探測一個節點,用以判斷該節點是否仍然在線。

(2)store操做,做用是通知一個節點存儲一個<key,value>對,以便之後查詢須要。

(3)find_node操做,做用是從本身的「路由表」對應的K桶中返回k個節點信息(IP address,UDP port,Node ID)給發送者

(4)find_value 操做,做用是把info-hash做爲參數,若是本操做接收者正好存儲了info-hash的peers則返回peers list,不然從本身的「路由表「中返回離info-hash更近的k個節點信息(同find_node過程)。

上面只是最基本的操做,一次nodes或者info-hash的查找lookup過程則須要節點進行若干次上面的find操做的,一個遞歸查找的過程。利用上面的操做更精確的描述一次一個節點x要查找ID值爲t 的節點, 過程以下:

一、 計算到t 的距離:d(x,y) = x⊕y

二、 從x 的第[㏒ d]個K 桶中取出α 個節點的信息(各個實現α值不同,有些是3有些則等於k值),同時進行FIND_NODE 操做。若是這個K 桶中的信息少於α 個,則從附近多個桶中選擇距離最

接近d 的總共α個節點。

三、 對接受到查詢操做的每一個節點,若是發現本身就是t,則回答本身是最接近t 的。不然測量本身和t 的距離,並從本身對應的K 桶中選擇α 個節點的信息給x。

四、 X 對新接受到的每一個節點都再次執行FIND_NODE 操做,此過程不斷重複執行,直到

每個分支都有節點響應本身是最接近t 的,或者說FIND_NODE操做返回的節點值沒有都已經被查找過了,即找不到更近的節點了。

五、 經過上述查找操做,x 獲得了k 個最接近t 的節點信息。

注意:這裏用「最接近」這個說法,是由於ID 值爲t 的節點不必定存在網絡中,也就是說t 沒有分配給任何一臺電腦。

查找peers-list的過程則換成find_value動做,但注意前文提到的區別便可以有相似的描述。

-----------------------------------------------------------------------------

協議

Kad定義了節點之間的交互協議。這些協議支撐了整個DHT網絡裏信息分佈式存儲的實現。這些協議都是使用UDP來傳送。其協議格式使用一種稱爲bencode的編碼方式來編碼協議數據。bencode是一種文本格式的編碼,它還用於種子文件內的信息編碼。

Kad協議具體格式可參考BitTorrent的定義:DHT Protocol。這些協議包括4種請求:ping,find_node,get_peer,announce_peer。在有些文檔中這些請求的名字會有不一樣,例如announce_peer又被稱爲store,get_peer被稱爲find_value。這4種請求中,都會有對應的迴應消息。其中最重要的消息是get_peer,其目的在於在網絡中查找某個資源對應的peer列表。

值得一提的是,全部這些請求,包括各類迴應,均可以用於處理該消息的節點構建路由表。由於路由表本質就是存儲網絡中的節點信息。

ping

用於肯定某個節點是否在線。這個請求主要用於輔助路由表的更新。

find_node

用於查找某個節點,以得到其地址信息。當某個節點接收到該請求後,若是目標節點不在本身的路由表裏,那麼就返回離目標節點較近的K個節點。這個消息可用於節點啓動時構建路由表。經過find_node方式構建路由表,其實現方式爲向DHT網絡查詢本身。那麼,接收該查詢的節點就會一直返回其餘節點了列表,查詢者遞歸查詢,直到沒法查詢爲止。那麼,何時沒法繼續查詢呢?這一點我也不太清楚。每一次查詢獲得的都是離目標節點更接近的節點集,那麼理論上通過若干次遞歸查詢後,就沒法找到離目標節點更近的節點了,由於最近的節點是本身,但本身還未徹底加入網絡。這意味着最後全部節點都會返回空的節點集合,這樣就算查詢結束?

實際上,經過find_node來構建路由表,以及順帶加入DHT網絡,這種方式何時中止在我看來並不重要。路由表的構建並不須要在啓動時構建完成,在之後與其餘節點的交互過程當中,路由表自己就會慢慢地獲得構建。在初始階段儘量地經過find_node去與其餘節點交互,最大的好處無非就是儘早地讓網絡中的其餘節點認識本身。

get_peer

經過資源的infohash得到資源對應的peer列表。當查詢者得到資源的peer列表後,它就能夠經過這些peer進行資源下載了。收到該請求的節點會在本身的路由表中查找該infohash,若是有收錄,就返回對應的peer列表。若是沒有,則返回離該infohash較近的若干個節點。查詢者若收到的是節點列表,那麼就會遞歸查找。這個過程同find_node同樣。

值得注意的是,get_peer的迴應消息裏會攜帶一個token,該token會用於稍後的announce_peer請求。

announce_peer

該請求主要目的在於通知,通知其餘節點本身開始下載某個資源。這個消息用於構建網絡中資源的peer列表。當一個已經加入DHT網絡的P2P客戶端經過種子文件開始下載資源時,首先在網絡中查詢該資源的peer列表,這個過程經過get_peer完成。當某個節點從get_peer返回peer時,查詢者開始下載,而後經過announce_peer告訴返回這個peer的節點。

announce_peer中會攜帶get_peer迴應消息裏的token。關於這一點,我有一個疑問是,在P2P中DHT網絡介紹文檔中提到:

(announce_peer)同時會把本身的peer信息發送給先前的告訴者和本身K桶中的k個最近的節點存儲該peer-list信息

無論這裏提到的K的最近的節點是離本身最近,仍是離資源infohash最近的節點,由於處理announce_peer消息時,有一個token的驗證過程。可是這K個節點中,並無在以前建立對應的token。我經過transmission中的DHT實現作了個數據收集,能夠證實的是,announce_peer消息是不只僅會發給get_peer的迴應者的。

------------------------------------------------------------------------------------

DHT爬蟲

DHT爬蟲是一個遵循Kad協議的假節點程序。

這個爬蟲的實現方式,主要包含如下內容:

  • 經過其餘節點的announce_peer發來的infohash確認網絡中有某個資源可被下載
  • 經過從網絡中獲取這個資源的種子文件,來得到該資源的描述

經過累計收集獲得的資源信息,就能夠提供一個資源搜索引擎,或者構建資源統計信息。如下進一步描述實現細節。整個爬蟲的實現依賴了一個很重要的信息,那就是資源的infohash實際上就是一個磁力連接(固然須要包裝一下數據)。這意味着一旦咱們得到了一個infohash,咱們就等於得到了一個種子。

得到資源通知

當爬蟲程序加入DHT網絡後,它總會收到其餘節點發來的announce_peer消息。announce_peer消息與get_peer消息裏都帶了資源的infohash,可是get_peer裏的infohash對應的資源在該網絡中不必定存在,即該資源沒有任何可用peer。而announce_peer則表示已經確認了該網絡中有節點正在下載該資源,也即該資源的數據確實存在該網絡中。

因此,爬蟲程序須要盡最大努力地獲取其餘節點發來的announce_peer消息。若是announce_peer消息會發送給離消息發送節點較近的節點,那麼,一方面,爬蟲程序應該將本身告訴網絡中儘量多的節點。這能夠經過一次完整的find_node操做實現。另外一方面,爬蟲程序內部實現能夠部署多個DHT節點,總之目的在於儘量地讓爬蟲程序稱爲其餘節點的較近者。

當收集到infohash以後,爬蟲程序還須要經過該infohash得到對應資源的描述信息。

獲取資源信息

得到資源描述信息,其實就是經過infohash得到對應的種子文件。這須要實現P2P協議裏的文件分享協議。種子文件的獲取其實就是一個文件下載過程,下載到種子文件以後,就能夠獲取到資源描述。這個過程一種簡單的方法,就是從infohash構建出一個磁力連接,而後交給一個支持磁力下載的程序下載種子。

從infohash構建出磁力連接很是簡單,只須要將infohash編碼成磁力連接的xt字段便可,構建實現能夠從transmission源碼裏找到.

如今你就能夠作一個實驗,在transmission的DHT實現中,在announce_peer消息的處理代碼中,將收到的infohash經過上面的appendMagnet轉換爲磁力連接輸出到日誌文件裏。而後,能夠經過支持磁力連接的程序(例如QQ旋風)直接下載。有趣的是,當QQ旋風開始下載該磁力連接對應的種子文件時,你本身的測試程序能收到QQ旋風程序發出的announce_peer消息。固然,你得想辦法讓這個測試程序儘量地讓其餘節點知道你,這能夠經過不少方式實現。

UPDATE

經過詳細閱讀transmission裏的DHT實現,一些以前的疑惑隨之解開。

announce_peer會發給哪些節點

在一次對infohash的查詢過程當中,全部對本節點發出的get_peer做出迴應的節點(不論這個迴應節點回應的是nodes仍是peers),當本節點取得peer信息時,就會對全部這些節點發出announce_peer。get_peer的迴應消息裏,不管是peer仍是node,都會攜帶一個token,這樣在未來收到對方的announce_peer時,就能夠驗證該token。

節點和bucket狀態

在本地的路由表中,保存的node是有狀態之分的。狀態分爲三種:good/dubious/bad。good節點基本能夠判定該節點是一個在線的而且能夠正常回應消息的節點;而bad節點則是可肯定的無效節點,一般會盡快從路由表中移除;而dubious則是介於good和bad節點之間,表示可能有問題的節點,須要進一步發送例如ping消息來確認其狀態。路由表中應該儘量保證保存的是good節點,對查詢消息的迴應裏也需攜帶好的節點。

bucket也是有狀態的,當一個bucket中的全部節點在必定時間以內都沒有任何活動的時候,該bucket則應該考慮進行狀態的確認,確認方式能夠隨機選擇該bucket中的節點進行find_node操做(這也是find_node除了用於啓動以外的惟一做用,但具體實現不見得使用這種方式)。沒有消息來往的bucket則應該考慮移除。DHT中幾乎全部操做都會涉及到bucket的索引,若是索引到一個全部節點都有問題的bucket,那麼該操做可能就沒法完成。

search在什麼時候中止

首先,某次發起的search,不管是對node仍是對peer,均可能致使進一步產生若干個search。這些search都是基於transaction id來標識的。由一次search致使產生的全部子search都擁有相同的transaction id,以使得在該search成功或失敗時能夠經過該transaction id來刪除對應的全部search。transaction id也就是DHT中每一個消息消息頭」t」的值。

可是search什麼時候中止?transmission中是經過超時機制來中止。在search過程當中,若是長時間沒有收到跟該search關聯的節點發來的迴應消息,那麼就撤銷該search,表示搜索失敗。

參考資料

---------------------------------------------------------

再告訴你們一個神奇的方法

有了HASH值能夠去   試下是否能夠播放等功能,輸入magnet:?xt=urn:btih:13ce77b3b934b12dc77fded6646426a6db5c3428就能夠播放了;

另外求服務器進行程序測試,須要有固定IP,10G的WIN服務器空間,h31h31@163.com,謝謝.

ps:本人開源程序的目的只是你們交流,若是有什麼違法的行爲與本人無關.

但願你們多多推薦哦...你們的推薦纔是下一篇介紹的動力...

相關文章
相關標籤/搜索