天天看點

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

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

先說下運作方法:

1)有固定IP的朋友可以試試H31DHT.exe資料抓取程式,會擷取一些資料,如果>2小時還沒有資料傳回,直接說明不是固定IP的傳回資料很少;

2)直接從http://torrage.com/sync下載下傳幾個文本檔案回來,放到程式目錄下,H31DHTMgr程式會自動周遊這個檔案夾取HASH檔案,

存儲到資料庫中,如果将此網站的200多萬資料(個人估計的)全部下載下傳成功,那也可以搜尋很多内容了.

大家可能問目前的程式采用什麼方法下載下傳BT種子的比較關心,下面就自己的體會給大家說說:

DHT磁力種子其實就是20位元組的HASH值,這個值可以直接從很多網站下載下傳種子,舉例子說明:

比如說上一篇檔案中有那麼多HASH值的字元串

繼續接着上一篇寫:使用C#實作DHT磁力搜尋的BT種子後端管理程式+資料庫設計(開源)[搜片神器]

昨天由于開源的時候沒有注意運作環境,直接沒有考慮下載下傳BT種子檔案時生成子檔案夾,可能導緻有的朋友運作沒有結果,在此表示對支援開源的朋友道謙.另外也對源程式增加了一些說明,已經送出.

開源位址:https://github.com/h31h31/H31DHTMgr

程式下載下傳:H31DHT下載下傳

個人電腦編譯環境是WIN7+VS2005,如果程式運作出錯,請自行下載下傳代碼進行編譯.

3)新程式界面如下:需要的自己下載下傳源代碼進行編譯(VS2005),不提供EXE下載下傳,希望大家感興趣的一起開源下;

[增加了複制磁連結和下載下傳選中項目的代碼,之前隻能檢視,不能顯示.]

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

--------------------先來點大家感興趣的東西-------------------------------------

 大家可能問目前的程式采用什麼方法下載下傳BT種子的比較關心,下面就自己的體會給大家說說:

比如說上一篇檔案中有那麼多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網絡中活躍的資源索引(活躍的資源意味着該網絡中肯定有人至少持有該資源的部分資料),還可以分析出該網絡中的熱門分享資源。網絡上其實也有其他人做了類似的應用:DHTmonitoring,Crawling 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棵子樹,每一個子樹和該節點都有一個共同的字首,共同字首越少離得越遠。如下圖所示:

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

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

以上圖紅色節點位例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 的節點, 過程如下:

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

2、 從x 的第[㏒ d]個K 桶中取出α 個節點的資訊(各個實作α值不一樣,有些是3有些則等于k值),同時進行FIND_NODE 操作。如果這個K 桶中的資訊少于α 個,則從附近多個桶中選擇距離最

接近d 的總共α個節點。

3、 對接受到查詢操作的每個節點,如果發現自己就是t,則回答自己是最接近t 的。否則測量自己和t 的距離,并從自己對應的K 桶中選擇α 個節點的資訊給x。

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

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

5、 通過上述查找操作,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,表示搜尋失敗。

參考資料

  • DHT Protocol
  • P2P中DHT網絡介紹
  • Torrent檔案結構解析
  • BitDHT源碼
  • Transmission DHT源碼
  • bencode
  • magnet
  • Crawling Bittorrent DHT

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

再告訴大家一個神奇的方法

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

另外求伺服器進行程式測試,需要有固定IP,10G的WIN伺服器空間,[email protected],謝謝.

ps:本人開源程式的目的隻是大家交流,如果有什麼違法的行為與本人無關.

希望大家多多推薦哦...大家的推薦才是下一篇介紹的動力...