不是吧!你还不懂DHT协议?
目錄
- 前文回顧
- 前提說明
- 概述
- 尋找節點的過程
- Token令牌
- 路由表
- KRPC協議
- 請求
- 回復
- 錯誤
- DHT中的4種請求
- ping
- find_node
- get_peers
- 補充知識
- announce_peer
- DHT網絡爬蟲和Python爬蟲的區別
前文回顧
如果還不懂DHT網絡,可以先看這篇文章。理解了DHT網絡,后面的內容才看得明白。
別再裝純說不懂BT種子了
前提說明
BitTorrent使用了“分布式哈希表”(DHT)為沒有Tracker的種子(torrent)存儲了peer之間的聯系信息。這樣每個peer都成了Tracker。
DHT是基于kademlim網絡,并且在UDP上實現的。
DHT 由節點組成,它存儲了 peer 的位置。BitTorrent 客戶端包含一個 DHT 節點,這個節點用來聯系 DHT 中其他節點,從而得到 peer 的位置,進而通過 BitTorrent 協議下載。
解釋說明
“peer”: 在一個 TCP 端口上監聽的客戶端/服務器,它實現了 BitTorrent 協議。簡單理解就是一臺電腦,但是端口號是基于TCP連接的
“節點”: 在一個 UDP 端口上監聽的客戶端/服務器,它實現了 DHT(分布式哈希表) 協議。簡單理解也是一臺電腦,但是端口號是基于UDP連接的
“BitTorrent客戶端”:指的是迅雷這些BT軟件等
概述
每個節點有一個全局唯一的標識符,作為 “node ID”。
每個節點都維護一個路由表,路由表中包含一部分節點的信息。每個節點都知道在DHT網絡中離自己很近的節點,離自己很遠的節點知道的很少。
尋找節點的過程
1、 當節點要為種子尋找peer時,它將自己的節點的哈希值(40位16進制字符)和種子的哈希值進行距離計算(異或算法)
2、 向路由表中離種子最近的節點發送請求,問他們正在下載種子的peer的信息
3、 被聯系的節點如果知道下載種子的peer信息,那它將peer的信息回復給當前的節點。如果不知道,將回復離種子最近的peer的節點信息,讓當前節點去請求離種子最近的peer。
4、 重復3步驟,直到不能找到離種子更近的節點信息。
5、 在查詢完之后,客戶端把自己作為peer信息,插入到所有回復節點中離種子最近的那個節點中。
Token令牌
如果一個節點宣布它所控制的 peer 正在下載一個種子,它必須在回復節點的同時,附加上對方向我們發送的最近的”令牌(token)”。Token令牌是用來核對信息的。主要體現在get_peer和announce_peer中,后面還會介紹。
路由表
每個節點都維護一個路由表,這個路由表保存著已知的好節點。路由表中的節點作為DHT請求的起始點。
這里的好節點是指在過去的 15 分鐘以內,曾經對我們的某一個請求給出過回復的節點,或者曾經對我們的請求給出過一個回復(不用在15分鐘以內),并且在過去的 15 分鐘給我們發送過請求。
還記得桶的概念嗎?桶里裝的都是好節點,一旦某個節點變壞了,我們就會用好的節點替代它。怎么確定是壞的節點呢?我們會向它發送ping請求,給出回復的是好的節點。
KRPC協議
KRPC協議是由bencode編碼組成RPC結構,使用UDP報文發送。
包含3種消息:請求、回復、錯誤
在DHT協議中,請求又分為四種:ping、find_node、get_peers、announce_peer
一條KRPC 消息由一個獨立的字典組成,其中有 2 個關鍵字是所有的消息都包含的,其余的附加關鍵字取決于消息類型。
每條消息都包含 t 關鍵字,它是一個代表了 transaction ID 的字符串類型。transaction ID 由請求節點產生,并且回復中要包含回顯該字段,所以回復可能對應一個節點的多個請求。簡單理解transaction ID就是一個請求的唯一標識
另外每個 KRPC 消息還應該包含的關鍵字是 y,它由一個字節組成,表明這個消息的類型。y 對應的值有三種情況:q 表示請求,r 表示回復,e 表示錯誤。
請求
請求,對應于 KPRC 消息字典中的 y 關鍵字的值是 q,它包含 2 個附加的關鍵字 q 和 a。關鍵字 q 是字符串類型,包含了請求的方法名字。(請求方法的名字就是ping、find_node、get_peers、announce_peer)關鍵字 a 一個字典類型包含了請求所附加的參數。
回復
回復,對應于 KPRC 消息字典中的 y 關鍵字的值是 r,包含了一個附加的關鍵字 r。關鍵字 r 是字典類型,包含了返回的值。發送回復消息是在正確解析了請求消息的基礎上完成的。
錯誤
錯誤,對應于 KPRC 消息字典中的 y 關鍵字的值是 e,包含一個附加的關鍵字 e。關鍵字 e 是列表類型。第一個元素是數字類型,表明了錯誤碼。第二個元素是字符串類型,表明了錯誤信息。當一個請求不能解析或出錯時,錯誤包將被發送。
DHT中的4種請求
所有的請求都包含一個關鍵字 id,它包含了請求節點的節點 ID。所有的回復也包含關鍵字 id,它包含了回復節點的節點 ID。
說明
節點ID(20位字節的字符串)和節點的哈希值即node節點ID(40位16進制的字符串)是不同的,不要搞混了。
ping
最基礎的請求就是 ping。這時 KPRC 協議中的 “q” = “ping”。
Ping 請求包含一個參數 id,它是一個 20 字節的字符串包含了發送者網絡字節序的節點 ID。
對應的 ping 回復也包含一個參數 id,包含了回復者的節點 ID。
報文示例
ping Query = {"t":"aa", "y":"q","q":"ping", "a":{"id":"abcdefghij0123456789"}} Response = {"t":"aa", "y":"r", "r": {"id":"mnopqrstuvwxyz123456"} }find_node
find_node 被用來查找給定 ID 的節點的聯系信息。這時 KPRC 協議中的 “q” == “find_node”。
find_node 請求包含 2 個參數,第一個參數是 id,包含了請求節點的ID。第二個參數是 target,包含了請求者正在查找的節點的 ID。
當一個節點接收到了 find_node 的請求,他應該給出對應的回復,回復中包含 2 個關鍵字 id 和 nodes,nodes 是字符串類型,包含了被請求節點的路由表中最接近目標節點的 K(8) 個最接近的節點的聯系信息。
報文示例
find_node Query = {"t":"aa", "y":"q","q":"find_node", "a": {"id":"abcdefghij0123456789","target":"mnopqrstuvwxyz123456"}} Response = {"t":"aa","y":"r","r": {"id":"0123456789abcdefghij","nodes": "def456..."}}get_peers
get_peers 與 torrent 文件的 infohash 有關。
這時 KPRC 協議中的 “q” = “get_peers”。get_peers 請求包含 2 個參數。第一個參數是 id,包含了請求節點的 ID。第二個參數是 info_hash,它代表 torrent 文件的 infohash。
如果被請求的節點有對應 info_hash 的 peers,他將返回一個關鍵字 values,這是一個列表類型的字符串。每一個字符串包含了 “CompactIP-address/portinfo” 格式的 peers 信息。如果被請求的節點沒有這個 infohash 的 peers,那么他將返回關鍵字 nodes,這個關鍵字包含了被請求節點的路由表中離 info_hash 最近的 K 個節點,使用 “Compactnodeinfo” 格式回復。
在這兩種情況下,關鍵字 token 都將被返回。token 關鍵字在今后的 annouce_peer 請求中必須要攜帶。token 是一個短的二進制字符串。
報文格式
get_peers Query = {"t":"aa", "y":"q", "q":"get_peers", "a": {"id":"abcdefghij0123456789", "info_hash":"mnopqrstuvwxyz123456"}} Response with peers = {"t":"aa","y":"r", "r": {"id":"abcdefghij0123456789","token":"aoeusnth","values": ["axje.u", "idhtnm"]}} Response with closest nodes = {"t":"aa","y":"r","r": {"id":"abcdefghij0123456789", "token":"aoeusnth","nodes": "def456..."}}補充知識
聯系信息編碼 Contact Encoding
Peers 的聯系信息被編碼為 6 字節的字符串。又被稱為 “CompactIP-address/port info”,其中前 4 個字節是網絡字節序的 IP 地址,后 2 個字節是網絡字節序的端口。
節點的聯系信息被編碼為 26 字節的字符串。又被稱為 “Compactnode info”,其中前 20 字節是網絡字節序的節點 ID,后面 6 個字節是 peers 的 “CompactIP-address/port info”
announce_peer
這個請求用來表明發出 announce_peer 請求的節點,正在某個端口下載 torrent 文件。
announce_peer 包含 4 個參數。
第一個參數是 id,包含了請求節點的 ID;
第二個參數是 info_hash,包含了 torrent 文件的 infohash;
第三個參數是 port 包含了整型的端口號,表明 peer 在哪個端口下載;
第四個參數數是 token,這是在之前的 get_peers 請求中收到的回復中包含的。
收到 announce_peer 請求的節點必須檢查這個 token 與之前我們回復給這個節點 get_peers 的 token 是否相同。如果相同,那么被請求的節點將記錄發送 announce_peer 節點的 IP 和請求中包含的 port 端口號在 peer 聯系信息中對應的 infohash 下
報文示例
announce_peers Query = {"t":"aa","y":"q", "q":"announce_peer", "a": {"id":"abcdefghij0123456789", "implied_port": 1, "info_hash":"mnopqrstuvwxyz123456", "port": 6881, "token": "aoeusnth"}} Response = {"t":"aa","y":"r", "r": {"id":"mnopqrstuvwxyz123456"}}DHT網絡爬蟲和Python爬蟲的區別
要想成功編寫出DHT網絡爬蟲,DHT網絡協議必須弄明白。
DHT爬蟲,就是把自己偽裝成DHT網絡中的一個節點,當某個客戶端想要下載某個torrent文件時,就會在DHT網絡上發起廣播,當它詢問我的節點時,我就知道:哦,原來有人下載這個種子,那么在DHT網絡上肯定有這個種子。于是我把這個種子的信息保存到我的數據庫。
Python 爬蟲,是主動出擊,盲目尋找。在互聯網的海量網頁中尋找種子和磁力鏈接。而 DHT 爬蟲則變成了被動等待,當別人來詢問時,就把它的詢問結果記錄下來,如果一個種子被詢問了很多次,則說明這個種子是一個熱門種子,這是 Python 爬蟲無法做到的。
簡單總結:python爬蟲是主動出擊,而DHT爬蟲是被動等待。
總結
以上是生活随笔為你收集整理的不是吧!你还不懂DHT协议?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 伪随机生成器具体实现——线性同余法
- 下一篇: STM32F4应用笔记(二)利用蜂鸣器播