經過不停止的查找相關資料,基本上實現DHT網絡的基本操作。現記錄下來,供以後參考。
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列表。
值得一提的是,所有這些請求,包括各種回應,都可以用於處理該消息的節點構建路由表。因為路由表本質就是存儲網絡中的節點信息。
用於確定某個節點是否在線。這個請求主要用於輔助路由表的更新。
用於查找某個節點,以獲得其地址信息。當某個節點接收到該請求後,如果目標節點不在自己的路由表裡,那麼就返回離目標節點較近的K個節點。這個消息可用於節點啟動時構建路由表。通過find_node方式構建路由表,其實現方式為向DHT網絡查詢自己。那麼,接收該查詢的節點就會一直返回其他節點了列表,查詢者遞歸查詢,直到無法查詢為止。那麼,什麼時候無法繼續查詢呢?這一點我也不太清楚。每一次查詢得到的都是離目標節點更接近的節點集,那麼理論上經過若干次遞歸查詢後,就無法找到離目標節點更近的節點了,因為最近的節點是自己,但自己還未完全加入網絡。這意味著最後所有節點都會返回空的節點集合,這樣就算查詢結束?
實際上,通過find_node來構建路由表,以及順帶加入DHT網絡,這種方式什麼時候停止在我看來並不重要。路由表的構建並不需要在啟動時構建完成,在以後與其他節點的交互過程中,路由表本身就會慢慢地得到構建。在初始階段盡可能地通過find_node去與其他節點交互,最大的好處無非就是盡早地讓網絡中的其他節點認識自己。
通過資源的infohash獲得資源對應的peer列表。當查詢者獲得資源的peer列表後,它就可以通過這些peer進行資源下載了。收到該請求的節點會在自己的路由表中查找該infohash,如果有收錄,就返回對應的peer列表。如果沒有,則返回離該infohash較近的若干個節點。查詢者若收到的是節點列表,那麼就會遞歸查找。這個過程同find_node一樣。
值得注意的是,get_peer的回應消息裡會攜帶一個token,該token會用於稍後的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的回應者的。
1、路由表的插入操作。
1)如果節點已經在路由表中,則更新節點,返回。
2)如果桶沒有滿,則插入,返回。
3)如果發現失效節點,替換,返回。
4)發現可疑節點,則保存新節點到緩存中並且如果該可疑節點沒有ping,發出ping_node操作,返回。
5)現在,桶已經充滿了好的節點,如果自己的ID沒有落在這個桶中,返回。
6)將桶空間分成兩半。跳到步驟1)。
2、KAD遠程處理調用。
這部分又分成3種,
1)ping/pong操作。
所有的包的tid都使用pg\0\0
2)find_node操作。
所有的包的tid都使用fn\0\0
3)get_peers/annouce_peer操作。
對同一個HASH的一次遞歸查詢中,tid保持不變。
其中只有3)種實現bittorrent的DHT規范裡面提到的遞歸查詢操作,1)和2)僅僅用來維護路由表,並且不保存狀態。
3、定時器處理:
為了檢測路由表中節點的有效性(根據規范,路由表中應該只保存有效節點),在代碼中,在執行krpc操作時如果發現時對路由表中的節點操作,那麼則保存操作的開始時間 pinged_time,通過操作的開始時間來判斷操作是否超時。
expire_stuff_time 超時時,會執行下面的操作:
1、檢查路由表中失效的節點(根據pinged_time來判定),並將該節點刪除。
2、檢查用來保存annoounce_peer的節點是否超過30分鐘(這個不打算深入討論,故不做解析)。
3、檢查遞歸查詢操作超時。
rotate_secrets_time 定時器。
用來每隔大約15分左右就更換token(見DHT規范).
confirm_nodes_time 定時器。
查找長期沒有活動的桶,然後通過執行一個find_node的krpc操作來刷新它。
search_time定時器。
有可能出現發出的所有的get_peers操作,都沒有應答,那麼search_time定時器遇到這種情形時負責重發所有請求。(注意: get_peers操作最大未決的krpc請求數是3)
用於維持路由表的ping/pong操作:
在試圖插入節點時,發現桶已經滿,而存在可疑節點時會觸發ping_node操作。未響應的節點會有可疑最終變為失效節點,而被替換。
下面介紹我們是如何進入DHT網絡
目前個人已經初步搭建了一個小網站SOSOBT.com,也正式增加了對磁力網站的了解。
後面有空繼續記錄DHT相關的資料。