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