bt协议详解 DHT篇(上)
bt協(xié)議詳解 DHT篇(上)
最近開(kāi)發(fā)了一個(gè)免費(fèi)教程的網(wǎng)站,突然產(chǎn)生了仔細(xì)了解bt協(xié)議的想法,這篇文章是bt協(xié)議詳解系列的第三篇,后續(xù)還會(huì)寫(xiě)一些關(guān)于搜索和索引的東西,都是在開(kāi)發(fā)這個(gè)網(wǎng)站的過(guò)程中學(xué)習(xí)到的技術(shù),敬請(qǐng)期待。
文章主要內(nèi)容來(lái)自于對(duì)DHT Protocol的翻譯,如果大家感興趣的話(huà),可以閱讀一下英文原版。
為了大家閱讀的方便,把文章分成了上下篇,兩篇加在一起快1w字了,確實(shí)看的比較累。
1 簡(jiǎn)介
前面講到BT協(xié)議像tcp/ip協(xié)議一樣是一個(gè)協(xié)議簇,dht協(xié)議在這個(gè)協(xié)議簇中出現(xiàn)的比較晚,但是它所發(fā)揮的作用卻不容小視。dht協(xié)議提出的一些新的想法讓我們能夠推翻基礎(chǔ)篇中的設(shè)計(jì),得到一個(gè)更簡(jiǎn)單更高效的bt服務(wù)器和bt客戶(hù)端。
在dht協(xié)議中,bt客戶(hù)端使用“distributed sloppy hash table”(DHT的全稱(chēng))來(lái)存儲(chǔ)沒(méi)有tracker地址的種子文件所對(duì)應(yīng)的peer節(jié)點(diǎn)的信息,在這種情況下,每一個(gè)peer節(jié)點(diǎn)變成了一個(gè)tracker服務(wù)器,dht協(xié)議是在udp通信協(xié)議的基礎(chǔ)上使用Kademila(俗稱(chēng)Kad算法)算法實(shí)現(xiàn)。
重要:注意這里使用的術(shù)語(yǔ),一個(gè)peer節(jié)點(diǎn)是一個(gè)實(shí)現(xiàn)了bt協(xié)議并且開(kāi)啟了tcp監(jiān)聽(tīng)端口的bt客戶(hù)端或者服務(wù)器。一個(gè)node節(jié)點(diǎn)是一個(gè)實(shí)現(xiàn)了dht協(xié)議并且開(kāi)啟了udp監(jiān)聽(tīng)端口的bt客戶(hù)端或者服務(wù)器,這兩者非常容易混淆。
dht由很多node節(jié)點(diǎn)以及這些node節(jié)點(diǎn)保存的peer地址信息組成,一個(gè)bt客戶(hù)端包括了一個(gè)dht node節(jié)點(diǎn),通過(guò)這些節(jié)點(diǎn)來(lái)和dht網(wǎng)絡(luò)中的其它節(jié)點(diǎn)通信來(lái)獲取peer節(jié)點(diǎn)的信息,然后再通過(guò)bt協(xié)議從peer節(jié)點(diǎn)下載文件。
看到這里大家應(yīng)該明白了,dht協(xié)議并不能取代bt協(xié)議,它只是bt協(xié)議的一個(gè)強(qiáng)有力的補(bǔ)充,在一些禁止運(yùn)行bt tracker服務(wù)器的國(guó)家,通過(guò)使用dht協(xié)議,用戶(hù)照樣可以下載想要的內(nèi)容。tracker服務(wù)器本來(lái)也不保存真正的文件,只是保存和torren文件相關(guān)的peer的信息,
dht協(xié)議通過(guò)從附近的node節(jié)點(diǎn)獲取peer信息,而不是從tracker服務(wù)器獲取peer信息,這就是所謂的trackerless。
2 概述
dht網(wǎng)絡(luò)中每一個(gè)node節(jié)點(diǎn)有一個(gè)全局的唯一標(biāo)識(shí),叫node ID(節(jié)點(diǎn)id),節(jié)點(diǎn)id是隨機(jī)從torrent種子文件中的160位的infohashes中隨機(jī)抽取的。distance metric(距離度量)用來(lái)比較兩個(gè)節(jié)點(diǎn)id或者節(jié)點(diǎn)id和infohash之間的距離。所有的節(jié)點(diǎn)(注意后面所有提到的node節(jié)點(diǎn)都簡(jiǎn)稱(chēng)節(jié)點(diǎn),peer節(jié)點(diǎn)不作簡(jiǎn)寫(xiě))必須保存一個(gè)routing table(路由表)保存它和dht網(wǎng)絡(luò)中一小部分節(jié)點(diǎn)交流的信息。離節(jié)點(diǎn)id越近的其它節(jié)點(diǎn)id的信息越詳細(xì)。所有的節(jié)點(diǎn)必須知道很多離它們很近的其它節(jié)點(diǎn),離它們很遠(yuǎn)的節(jié)點(diǎn)只需要有足夠的握手信息就行了。
在Kad算法中,距離度量是對(duì)兩個(gè)hash值進(jìn)行XOR(異或)運(yùn)算,并且把結(jié)果轉(zhuǎn)換成無(wú)符號(hào)整數(shù)。distance(A,B)=|A xor B|,結(jié)果值越小,距離越近。
當(dāng)一個(gè)節(jié)點(diǎn)想找到一個(gè)種子文件的peer節(jié)點(diǎn)信息時(shí),就使用距離算法把種子文件的infohash字段和它自己路由表中的節(jié)點(diǎn)id進(jìn)行比較,然后和距離最近的節(jié)點(diǎn)進(jìn)行通信,向它們發(fā)送請(qǐng)求獲取正在下載這個(gè)種子文件的peer節(jié)點(diǎn)列表的信息。如果它請(qǐng)求的節(jié)點(diǎn)知道這個(gè)種子文件的peer節(jié)點(diǎn)列表,則把peer節(jié)點(diǎn)列表返回給發(fā)送請(qǐng)求的節(jié)點(diǎn)。如果不知道,它必須返回自己路由表中離infohash最近的節(jié)點(diǎn)列表給請(qǐng)求者。原始節(jié)點(diǎn)不斷迭代的發(fā)送請(qǐng)求直到找到離目標(biāo)infohash更近的節(jié)點(diǎn)。搜索結(jié)束之后,bt客戶(hù)端把peer節(jié)點(diǎn)的信息保存在自己的路由表里面。
請(qǐng)求peer節(jié)點(diǎn)列表的返回值中包含了一個(gè)可選“token”,當(dāng)一個(gè)節(jié)點(diǎn)聲明它控制的peer節(jié)點(diǎn)正在下載一個(gè)種子文件時(shí),它必須把它最近發(fā)送請(qǐng)求中獲取的token返回向它發(fā)送請(qǐng)求的節(jié)點(diǎn)。當(dāng)一個(gè)節(jié)點(diǎn)嘗試“聲明”一個(gè)種子時(shí),被詢(xún)問(wèn)的節(jié)點(diǎn)把token和ip進(jìn)行檢查,這樣做是為了防止冒充其它主機(jī)下載文件。因?yàn)閠oken只是在查詢(xún)節(jié)點(diǎn)和它獲取token的節(jié)點(diǎn)之間發(fā)送,具體的實(shí)現(xiàn)沒(méi)有任何限制。tokens在發(fā)布之后的一段時(shí)間之內(nèi)是生效的。bittorrent客戶(hù)端使用秘鑰對(duì)ip進(jìn)行sha1哈希,秘鑰每5分鐘改變一次,生成的token在10分鐘內(nèi)是有效的。
3 路由表
每一個(gè)節(jié)點(diǎn)都維護(hù)一個(gè)路由表保存一些已知的通信好的節(jié)點(diǎn)。路由表中的節(jié)點(diǎn)通常用來(lái)作為起始節(jié)點(diǎn),當(dāng)其它節(jié)點(diǎn)向這個(gè)節(jié)點(diǎn)發(fā)送請(qǐng)求時(shí),路由表中的這些節(jié)點(diǎn)就會(huì)被返回給發(fā)送請(qǐng)求的幾點(diǎn)。
并不是每一個(gè)已知的節(jié)點(diǎn)都是對(duì)等的。一些節(jié)點(diǎn)是活躍的(原文是“good”)的,另外一些不是。dht中的許多節(jié)點(diǎn)可以發(fā)送請(qǐng)求和接受返回,但是不會(huì)響應(yīng)dht網(wǎng)絡(luò)中其它節(jié)點(diǎn)的請(qǐng)求。每一個(gè)節(jié)點(diǎn)的路由表中都只保存好的節(jié)點(diǎn),這一點(diǎn)非常重要。一個(gè)活躍的節(jié)點(diǎn)就是能在15分鐘之內(nèi)響應(yīng)過(guò)請(qǐng)求或者在15分鐘之內(nèi)發(fā)送過(guò)請(qǐng)求的節(jié)點(diǎn)。15分鐘之內(nèi)沒(méi)有活動(dòng)的話(huà),這個(gè)節(jié)點(diǎn)變成問(wèn)題節(jié)點(diǎn)。當(dāng)一個(gè)節(jié)點(diǎn)響應(yīng)請(qǐng)求失敗的話(huà),它就變成壞的節(jié)點(diǎn)。活躍的節(jié)點(diǎn)比狀態(tài)不明的節(jié)點(diǎn)的優(yōu)先級(jí)要高(這不是顯然嗎?)。
路由表覆蓋從0到2160完整的nodeID空間。路由表又被劃分為buckets(桶),每一個(gè)bucket包含一個(gè)子部分的nodeID空間。一個(gè)空的路由表只有一個(gè)bucket,它的ID范圍從min=0到max=2160。當(dāng)一個(gè)nodeID為“N”的node插入到表中時(shí),它將被放到ID范圍在min< N < max的bucket中。一個(gè)空的路由表只有一個(gè)bucket所以所有的node都將被放到這個(gè)bucket中。每一個(gè)bucket最多只能保存K個(gè)nodes,當(dāng)前K=8。當(dāng)一個(gè)bucket放滿(mǎn)了好的nodes之后,將不再允許新的節(jié)點(diǎn)加入,除非我們自身的nodeID在這個(gè)bucket的范圍內(nèi)。在這樣的情況下,這個(gè)bucket將被分裂為2個(gè)新的buckets,每一個(gè)新桶的范圍都是原來(lái)舊桶的一半。原來(lái)舊桶中的nodes將被重新分配到這兩個(gè)新的buckets中。如果是一個(gè)只有一個(gè)bucket的新表,這個(gè)包含整個(gè)范圍的bucket將總被分裂為2個(gè)新的buckets,第一個(gè)的覆蓋范圍從0..2159,第二個(gè)的范圍從2159..2160。
當(dāng)bucket裝滿(mǎn)了好的nodes,那么新的node將被丟棄。一旦bucket中的某一個(gè)node變?yōu)榱藟牡膎ode,那么我們就用新的node來(lái)替換這個(gè)壞的node。如果bucket中有在15分鐘內(nèi)都沒(méi)有活躍過(guò)的節(jié)點(diǎn),我們將這樣的節(jié)點(diǎn)視為可疑的節(jié)點(diǎn),這時(shí)我們向最久沒(méi)有聯(lián)系的節(jié)點(diǎn)發(fā)送ping。如果被pinged的節(jié)點(diǎn)給出了回復(fù),那么我們向下一個(gè)可疑的節(jié)點(diǎn)發(fā)送ping,不斷這樣循環(huán)下去,直到有某一個(gè)node沒(méi)有給出ping的回復(fù),或者當(dāng)前bucket中的所有nodes都是好的(也就是所有nodes都不是可疑nodes,他們?cè)谶^(guò)去15分鐘內(nèi)都有活動(dòng))。如果bucket中的某個(gè)node沒(méi)有對(duì)我們的ping給出回復(fù),我們最好再試一次(再發(fā)送一次ping,因?yàn)檫@個(gè)node也許仍然是活躍的,但由于網(wǎng)絡(luò)擁塞,所以發(fā)生了丟包現(xiàn)象,注意DHT的包都是UDP的),而不是立即丟棄這個(gè)node或者直接用新node來(lái)替代它。這樣,我們得路由表將充滿(mǎn)穩(wěn)定的長(zhǎng)時(shí)間在線(xiàn)的nodes。
每一個(gè)bucket都應(yīng)該維持一個(gè)“l(fā)astchange”字段來(lái)表明bucket中的nodes有多新鮮。當(dāng)一個(gè)bucket中的node被ping并給出了回復(fù),或者一個(gè)node被加入到了bucket,或者一個(gè)node被一個(gè)新的node所替代,bucket的“l(fā)astchanged”字段都應(yīng)當(dāng)被更新。如果一個(gè)bucket的“l(fā)astchange”在過(guò)去的15分鐘內(nèi)都沒(méi)有變化,那么我們將更新它。這個(gè)更新bucket操作是這樣完成的:從這個(gè)bucket所覆蓋的范圍中隨機(jī)選擇一個(gè)ID,并對(duì)這個(gè)ID執(zhí)行find_nodes查找操作。常常收到請(qǐng)求的nodes通常不需要常常更新自己的buckets,反之,不常常收到請(qǐng)求的nodes常常需要周期性的執(zhí)行更新所有buckets的操作,這樣才能保證當(dāng)我們用到DHT的時(shí)候,里面有足夠多的好的nodes。
在第一個(gè)node插入路由表并開(kāi)始服務(wù)后,這個(gè)node應(yīng)該試著查找離自身更近的node,這個(gè)查找工作是通過(guò)不斷的發(fā)布find_node消息給越來(lái)越近的nodes來(lái)完成的,當(dāng)不能找到更近的節(jié)點(diǎn)時(shí),這個(gè)擴(kuò)散工作就結(jié)束了。路由表應(yīng)當(dāng)被啟動(dòng)工作和客戶(hù)端軟件保存(也就是啟動(dòng)的時(shí)候從客戶(hù)端中讀取路由表信息,結(jié)束的時(shí)候客戶(hù)端軟件記錄到文件中)。
4 bt協(xié)議擴(kuò)展
BitTorrent協(xié)議已經(jīng)被擴(kuò)展為可以在通過(guò)tracker得到的peer之間互相交換nodeUDP端口號(hào)(也就是告訴對(duì)方我們的DHT服務(wù)端口號(hào)),在這樣的方式下,客戶(hù)端可以通過(guò)下載普通的種子文件來(lái)自動(dòng)擴(kuò)展DHT路由表。新安裝的客戶(hù)端第一次試著下載一個(gè)無(wú)tracker的種子時(shí),它的路由表中將沒(méi)有任何nodes,這是它需要在torrent文件中找到聯(lián)系信息。
peers如果支持DHT協(xié)議就將BitTorrent協(xié)議握手消息的保留位的第八字節(jié)的最后一位置為1。這時(shí)如果peer收到一個(gè)handshake表明對(duì)方支持DHT協(xié)議,就應(yīng)該發(fā)送PORT消息。它由字節(jié)0x09開(kāi)始,payload的長(zhǎng)度是2個(gè)字節(jié),包含了這個(gè)peer的DHT服務(wù)使用的網(wǎng)絡(luò)字節(jié)序的UDP端口號(hào)。當(dāng)peer收到這樣的消息是應(yīng)當(dāng)向?qū)Ψ降腎P和消息中指定的端口號(hào)的node發(fā)送ping。如果收到了ping的回復(fù),那么應(yīng)當(dāng)使用上述的方法將新node的聯(lián)系信息加入到路由表中。
本文來(lái)自 免費(fèi)教程網(wǎng)
轉(zhuǎn)載于:https://my.oschina.net/ideras/blog/689669
總結(jié)
以上是生活随笔為你收集整理的bt协议详解 DHT篇(上)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: c语言同余法随机数,线性同余法取随机数
- 下一篇: ffmpeg 推流