DHT(分散ハッシュテーブル)分布式ハッシュ表は、分散計算システムの一種であり、ハッシュテーブルに似た検索サービスを提供する分散システムです。キーと値のペアは DHT 分散ハッシュテーブルに保存され、参加している任意のノードは、与えられたキーに関連付けられた値を効率的に取得できます。BitTorrent と Magnet の原理と比較に関する記事では、彼らの関係と違いについて紹介しましたが、ここでは DHT プロトコルが具体的に何であるかを詳しく説明します。
DHT の主な利点は、ノードを追加または削除できることで、最小限のキー再配布作業で済むことです。キーは特定の値にマッピングされるユニークな識別子であり、特定の値はアドレスから文書、任意のデータまで何でもあり得ます。キーと値のマッピングを維持する責任はノード間で分散され、関与する参加者のいかなる変更もシステムやプロセスの全体的な機能に与える影響を最小限に抑えます。これにより、DHT は非常に大量のノードに拡張でき、ノードの到着、離脱、障害に対応できます。
BitTorrent は「分散型ルーズハッシュテーブル」(DHT)を使用して、トラッカーレスプロトコルに基づくシードノードの接続情報を保存します。実際、各ノードはトラッカーになります。このプロトコルは Kademlia に基づいており、UDP を介して実装されています。
この文書で使用される用語に注意してください。混乱を避けるためです。Peer ノードは、BitTorrent プロトコルを実装する TCP ポートでリスニングしているクライアント / サーバーです。Node ノードは、分散ハッシュテーブルプロトコルを実装する UDP ポートでリスニングしているトラッカーのクライアント / サーバーです。DHT は Node ノードと Peer を保存する位置ノードで構成されています。BitTorrent クライアントには DHT ノードが含まれており、このノードは DHT 内の他の Node ノードと連絡を取り、BitTorrent プロトコルを使用してダウンロードされる Peer ノードの位置を取得します。
Peer は通常、ファイル共有に参加するユーザー端末、つまり特定のファイルをダウンロードまたはアップロードするクライアントプログラムを指します。これらは通常、同じレベルにあります。各 Peer はファイルをダウンロードおよびアップロードでき、これによりネットワーク全体がより分散化されます。なぜなら、どのノードもすべてのデータを掌握していないからです。Node は通常、トラッカーサーバーを指し、トラッカープロトコルを介して各 Peer 間の接続とデータ転送を管理および調整します。トラッカーサーバーは、アクティブな Peer のリストを維持し、他の Peer に送信して接続を確立し、ファイルを共有できる他の Peer を見つけるのを助けます。したがって、BitTorrent では、Peer はファイルをダウンロードおよびアップロードするクライアントプログラムを指し、Node は Peer 間の接続とファイル共有を調整するためのトラッカーサーバーを指します。
各 Node ノードには、Node ID と呼ばれるグローバルに一意の識別子があります。Node ID は、BitTorrent 情報ハッシュと同じ 160 ビットの空間からランダムに選択されます。距離指標は、2 つの Node ID または Node ID と近接度を比較する情報ハッシュを比較するために使用されます。Node ノードは、少数の他のノードの連絡先情報を含むルーティングテーブルを維持する必要があります。ID がノード自身の ID に近づくにつれて、ルーティングテーブルはより詳細になります。ノードは DHT 内の多くの他のノードを知っており、これらのノードの ID は自身の ID に近いですが、ダウンロード者の ID が自身の ID から遠く離れているのはごくわずかです。
Kademlia では、距離測定は XOR であり、結果は符号なし整数として解釈されます。distance (A,B) = |A xor B| 値が小さいほど近いです。
Node ノードがシードの Peer ノードを探したい場合、距離指標を使用してシードの情報ハッシュを自身のルーティングテーブル内のノードの ID と比較します。次に、最も近い情報ハッシュの ID を使用して、知っているノードに連絡し、現在ダウンロード中のシードの Peer ノードのダウンロード情報を提供するように要求します。連絡したノードがシードの対等ノードを知っている場合、対等ノードの連絡情報は応答と共に返されます。そうでない場合、連絡したノードは、ルーティングテーブル内でシード情報ハッシュに最も近いノードの連絡情報を使用して応答する必要があります。元のノードは、目標情報ハッシュに近いノードを繰り返し問い合わせ、これ以上近いノードが見つからないまで続けます。検索が完了した後、クライアントは自身の対等ダウンロード者情報を、シード情報ハッシュに最も近い ID を持つ応答ノードに挿入します。
infohash は、トレントファイルを識別するための 40 の 16 進数文字で表されるユニークな識別子です。この識別子は、トレントファイルのメタデータハッシュを生成することによって生成され、ファイル名、ファイルサイズ、ファイル数などの情報が含まれています。クライアントがトレントファイルをダウンロードしたい場合、そのファイルの infohash を含むリクエストをトラッカーに送信します。トラッカーは、利用可能なシードと Peer のリストを含む応答を返し、このリストはクライアントが他のクライアントに接続し、ファイルをダウンロードを開始するのに役立ちます。infohash はファイルの内容に基づいて生成されるため、2 つのトレントファイルの名前が異なっていても、同じデータを含んでいる限り、それらの infohash は同じになります。
Peer ノードのクエリの戻り値には、トークンと呼ばれる不透明な値が含まれています。Node ノードが制御する Peer ノードがシードをダウンロードしていることを宣言するには、最近の Peer ノードのクエリで、同じクエリノードから受け取ったトークンを提供する必要があります。ノードがシードを発行しようとすると、問い合わせられたノードは、問い合わせノードの IP アドレスに基づいてトークンを確認します。トークンは、クエリノードからトークンを受け取った同じノードにのみ返されるため、トークンは配布後の合理的な時間内に受け入れられる必要があります。BitTorrent 実装は、IP アドレスの SHA1 ハッシュをトークンに接続し、このトークンは 5 分ごとに変更され、最大 10 分間受け入れられます。
BitTorrent では、「トークン」は通常、トラッカーサーバーによって認証された方法を指し、クライアントが他の対等者(Peers)を取得し、必要なファイルブロックをダウンロードするための一時的なキーまたはトークンを取得するのに役立ちます。BitTorrent クライアントが特定のトレントダウンロードに参加したい場合、最初にトラッカーにリクエストを送信し、利用可能な対等者のリストを取得します。トラッカーが認証を必要とする場合、特定のトレントダウンロードにのみ使用できる一時的なトークンとしてクライアントに「トークン」を返すことがあります。クライアントはこのトークンを使用して、トラッカーに対等者リストの更新やファイルブロックのダウンロードリクエストを送信できます。このトークンは、許可されていないクライアントや対等者がダウンロードに参加しようとするのを防ぎ、全体の BitTorrent ネットワークのセキュリティを強化します。
ルーティングテーブル
各ノードは、既知の良好なノードのルーティングテーブルを維持します。ルーティングテーブル内のノードは、DHT 内のクエリの出発点として使用されます。他のノードからのクエリに応じて、ルーティングテーブル内のノードが返されます。
私たちが知っているすべてのノードが平等であるわけではありません。良いノードもあれば、そうでないノードもあります。DHT を使用する多くのノードは、クエリを送信し、応答を受信できますが、他のノードからのクエリには応答できません。重要なのは、各ノードのルーティングテーブルには、既知の良好なノードのみが含まれている必要があることです。良いノードは、過去 15 分以内に私たちのクエリに応答したノードです。ノードが過去 15 分以内に私たちのクエリに応答し、私たちにクエリを送信した場合、それも良いノードです。非アクティブ状態が 15 分続くと、ノードは問題を抱えるようになります。ノードが連続して複数のクエリに応答できない場合、それらは悪化します。私たちは良好なノードが状態不明のノードよりも優先されることを知っています。
ルーティングテーブルは、0 から 2160 までのノード ID 空間全体をカバーします。ルーティングテーブルはバケットに細分化されており、各バケットは空間の一部をカバーします。空のテーブルには、ID 空間の範囲が min=0、max=2160 のバケットがあります。ID が「N」のノードがテーブルに挿入されると、そのノードは最小値が <= N < 最大値のバケットに配置されます。空のテーブルには 1 つのバケットしかないため、どのノードもその中に収まる必要があります。各バケットは K 個のノードを収容でき、現在は 8 個のノードで、「満杯」と見なされます。バケットが既知の良好なノードで満たされると、私たち自身のノード ID がバケットの範囲内でない限り、ノードを追加できなくなります。この場合、バケットは 2 つの新しいバケットに置き換えられ、各バケットの範囲は古いバケットの半分になり、古いバケット内のノードは 2 つの新しいバケットの間に分配されます。新しいテーブルにバケットが 1 つしかない場合、全体のバケットは常に 2 つの新しいバケットに分割され、範囲は 0..2159 と 2159..2160 になります。
BitTorrent プロトコルにおいて、バケットはダウンローダーがファイルをダウンロードする際に、ダウンロードタスクを複数のサブタスクに分割し、各サブタスクを異なるリモートアップローダー(peers)に割り当ててダウンロードするデータブロックを指します。これらのデータブロックはバケットに整理され、一連のデータブロックの集合と見なすことができます。通常、1 つのバケットには数百のデータブロックが含まれ、各データブロックのサイズは約 16KB から 64KB です。ダウンローダーは、複数のアップローダーから異なるバケットを取得し、ダウンロードを加速しようとします。この方法は、ダウンローダーが複数のソースから同時にデータをダウンロードするため、マルチソースダウンロード(multi-source downloading)とも呼ばれます。
バケットが良好なノードで満たされると、新しいノードは単純に破棄されます。既知のバケット内のノードが破損している場合、新しいノードが 1 つ置き換えられます。バケット内に疑わしいノードがあり、過去 15 分間にクエリされていない場合、最近最も少ないクエリのノードに ping 操作が実行されます。ping されたノードが応答した場合、次に最近最も少ないクエリの疑わしいノードに ping 操作が実行され、ノードが応答できなくなるか、バケット内のすべてのノードが既知の良好なノードになるまで続けられます。バケット内のノードが ping に応答できない場合、そのノードを破棄し、新しい良好なノードに置き換える前に再試行することをお勧めします。これにより、テーブルには安定した長期間稼働しているノードが埋め込まれます。
各バケットは、「最終変更」属性を維持する必要があり、内容の「新鮮さ」を示します。バケット内のノードが ping されて応答した場合、またはノードがバケットに追加された場合、またはバケット内のノードが別のノードに置き換えられた場合、バケットの最終変更属性を更新する必要があります。15 分間変更されていないバケットは「リフレッシュ」される必要があります。これは、バケット範囲内でランダムな ID を選択し、それに対して find_nodes 検索を実行することによって行われます。他のノードからのクエリを受信できるノードは、通常、バケットを頻繁にリフレッシュする必要はありません。他のノードからクエリを受信できないノードは、DHT が必要なときに良好なノードがテーブルに存在することを確認するために、すべてのバケットを定期的にリフレッシュする必要があります。
最初のノードをルーティングテーブルに挿入した後、ノードは DHT 内で自身に最も近いノードを探そうとする必要があります。これは、ますます近いノードに find_node メッセージを送信することによって実現され、より近いノードが見つからなくなるまで続けられます。ルーティングテーブルは、クライアントソフトウェアの呼び出し間で保存される必要があります。
BitTorrent プロトコルの拡張#
BitTorrent プロトコルは、トラッカーによって導入された Peer ノード間でノード UDP ポート番号を交換するように拡張されました。この方法により、クライアントは通常のシードをダウンロードすることで自動的にルーティングテーブルを作成できます。トラッカーレスシードを初めてダウンロードしようとする新しいインストールのクライアントのルーティングテーブルには、ノードが存在しません。シードファイルにもノード情報が必要です。
DHT をサポートする Peer ノードは、BitTorrent プロトコルのハンドシェイクで交換される 8 バイトの予約フラグの最終ビットを設定します。Peer ノードがリモート Peer ノードが DHT をサポートしていることを示すハンドシェイクを受信した場合、PORT メッセージを送信する必要があります。これはバイト 0x09 で始まり、DHT ノードの UDP ポートをネットワークバイト順に含む 2 バイトのペイロードを持っています。このメッセージを受信した Peer ノードは、リモート Peer ノードの受信ポートと IP アドレスでノードに ping を試みる必要があります。ping に応答があった場合、ノードは通常のルールに従って新しい連絡先情報をルーティングテーブルに挿入しようとします。
トレントファイルの拡張子#
トラッカーレスシード辞書には「リリース」キー値がありません。代わりに、トラッカーレスのシードには「ノード」キー値があります。このキー値は、シード生成クライアントのルーティングテーブル内の K 個の最近のノードに設定する必要があります。あるいは、生成シードの人が操作する既知の良好なノードに設定することもできます。「Router.Bittorrent.Com」をシードファイルに自動的に追加したり、このノードをクライアントのルーティングテーブルに自動的に追加したりしないでください。
nodes = [["<host>", <port>], ["<host>", <port>], ...]
nodes = [["127.0.0.1", 6881], ["your.router.node", 4804], ["2001:db8:100:0:d5c8:db3f:995e:c0f7", 1941]]
KRPC プロトコル#
KRPC プロトコルは、UDP を介して送信されるエンコードされた辞書から構成されるシンプルな RPC メカニズムです。単一のクエリデータパケットを送信し、単一のデータパケットを応答として送信し、再試行はありません。メッセージタイプは 3 種類あります:クエリ、応答、エラー。DHT プロトコルには、ping、find_node、get_peers、announce_peer の 4 つのクエリがあります。
各 KRPC メッセージは単一のハッシュテーブルであり、各メッセージには 3 つの一般的なキーがあり、メッセージタイプに応じて他のキー値が提供されます。各メッセージには「t」というキーがあり、その文字列値はトランザクション ID を示します。このトランザクション ID はクエリノードによって生成され、応答でエコーされるため、応答は同じノードに対する複数のクエリに関連付けられる可能性があります。トランザクション ID は、小さなバイナリ数字のストリングとしてエンコードされるべきであり、通常 2 文字で十分です。なぜなら、2^16 の未完了のクエリをカバーするからです。各メッセージには「y」というキーもあり、メッセージタイプを示す単一の文字値が含まれています。「y」キーの値は、「q」はクエリを示し、「r」は応答を示し、「e」はエラーを示します。クライアントバージョン文字列を持つ各メッセージには「v」というキーも含まれるべきです。ただし、すべての実装が「v」キーを含むわけではないため、クライアントはその存在を仮定すべきではありません。
連絡先エンコーディング#
Peer ノードの連絡先情報をコンパクトに表現するエンコーディング方式で、「圧縮 IP アドレス / ポート情報」とも呼ばれます。このエンコーディング方式では、Peer ノードの連絡先情報は 6 バイトの文字列として表されます。最初の 4 バイトはその Peer ノードの IP アドレスを示し、ネットワークバイト順(すなわちビッグエンディアン)でエンコードされます。後の 2 バイトはその Peer ノードのポート番号を示し、同様にネットワークバイト順でエンコードされます。具体的には、これら 6 バイトの配置順序は、最初に IP アドレスを示し、次にポート番号を示すため、結合された文字列は IP アドレス + ポート番号の形式になります。
例えば、Peer の IP アドレスが 192.168.1.100、ポート番号が 6881 の場合、その連絡先情報は 6 バイトの文字列として表すことができます:
c0 a8 01 64 1a e1
ここで、最初の 4 バイト c0 a8 01 64 は IP アドレス 192.168.1.100 を示し、後の 2 バイト 1a e1 はポート番号 6881 を示します。これら 2 つの部分は、IP アドレスが前、ポート番号が後の順序で結合され、6 バイトの文字列が得られます。
ノード情報は異なるエンコーディング方式を使用します。連絡先ノードの情報は、20 バイトのノード ID と 6 バイトの IP アドレスおよびポート番号情報を含む 26 バイトの文字列としてエンコードされます。このエンコーディング方式は「コンパクトノード情報」とも呼ばれます。ノードはこれらの情報を使用して他の対等者に接続し、ファイル共有を行うことができます。
クエリ#
リクエストを送信する際、メッセージ辞書の「y」キーは「q」に設定され、これはクエリリクエストであることを示します。さらに、メッセージ辞書には「q」と「a」の 2 つのキーが含まれる必要があります。「q」キーは具体的なクエリタイプを指定し、「a」キーはクエリに必要なパラメータを含みます。
応答#
KRPC プロトコルに従い、メッセージ辞書には「y」キーが含まれ、メッセージタイプを示します。その値が「r」である場合、これは応答メッセージを示します。この場合、メッセージ辞書には追加の「r」キーが含まれ、その値は命名された戻り値を含む辞書です。
ノードがリクエストメッセージを受信し、正常に処理した後、リクエスト元に応答メッセージを送信します。この応答メッセージは同じ形式を持ちますが、「y」キーの値は「r」であり、追加の「r」キーが含まれ、その値は応答の戻り値を含む辞書です。この方法により、ノード間でクエリと応答を行い、基本的な通信と相互作用を実現します。
エラー#
KRPC プロトコルにおいて、メッセージ辞書の「y」キーの値が「e」である場合、これはエラーメッセージを示します。この場合、メッセージ辞書には追加の「e」キーが含まれ、その値はリストです。このリストの最初の要素は整数で、エラーコードを示し、2 番目の要素はエラーメッセージを含む文字列です。この状況は、ノードがリクエストメッセージを受信したが処理できない場合や、何らかの理由でリクエストを完了できない場合に発生することがよくあります。エラーメッセージを送信することで、リクエスト元はエラーの具体的な理由を理解し、それに応じた対策を講じることができます。
下表は、可能なエラーコードを示しています:
エラーコード | エラー説明 |
---|---|
201 | 一般的なエラー |
202 | サーバー側のエラー |
203 | プロトコルエラー(例:フォーマットエラーのデータパケット、無効なパラメータ、エラーのトークン) |
204 | 方法が不明なエラー |
エラーデータパケットの例:
generic error = {"t":"aa", "y":"e", "e":[201, "A Generic Error Ocurred"]}
bencoded = d1:eli201e23:A Generic Error Ocurrede1:t2:aa1:y1:ee
DHT クエリ#
すべてのクエリおよび応答メッセージには「ID」キーが含まれ、そのキーの値にはクエリまたは応答を発信したノードの ID が含まれます。クエリメッセージの場合、リクエスト元は自身のノード ID を「ID」キーの値としてメッセージ辞書に追加します。受信ノードがクエリメッセージを受信すると、自身のノード ID を「ID」キーの値として応答メッセージに追加し、リクエスト元が応答を受信したときにどのノードからの応答であるかを特定できるようにします。
「ID」キーを使用することで、ノード間で基本的な通信と相互作用が確立され、各ノードが他のノードからのクエリおよび応答メッセージを識別し、追跡できるようになります。
Ping#
最も基本的なクエリは ping です。「q」=「ping」ping クエリには単一の引数「id」があり、その値はネットワークバイト順で送信者のノード ID を含む 20 バイトの文字列です。ping に対する適切な応答には、応答ノードのノード ID を含む単一のキー「id」が含まれます。
最も基本的なクエリは ping です。「q」=「ping」ping クエリには単一の引数「id」があり、その値はネットワークバイト順で送信者のノード ID を含む 20 バイトの文字列です。ping に対する応答には、応答ノードのノード ID を含む単一のキー「id」が含まれます。
KRPC プロトコルにおいて、「ping」は最も基本的なクエリタイプの 1 つであり、ターゲットノードがオンラインであり、正常に応答しているかを検出するために使用されます。「ping」クエリには、1 つのパラメータ「ID」が含まれ、その値は 20 バイトの文字列で、ネットワークバイト順で送信者ノードの ID を示します。「ping」クエリメッセージを受信すると、ノードは単にノード ID を含む応答メッセージを返信して確認します。この応答メッセージには、単一のキー「ID」が含まれ、その値は応答ノードの ID です。
「ping」クエリメッセージを送信し、応答メッセージを受信することで、ノードはターゲットノードがオンラインであるかどうかを確認し、接続の準備が整っているかを確認できます。これは P2P ネットワークにおける最も基本的な通信方法の 1 つです。
arguments: {"id" : "<querying nodes id>"}
response: {"id" : "<queried nodes id>"}
サンプルデータパケット
ping Query = {"t":"aa", "y":"q", "q":"ping", "a":{"id":"abcdefghij0123456789"}}
bencoded = d1:ad2:id20:abcdefghij0123456789e1:q4:ping1:t2:aa1:y1:qe
Response = {"t":"aa", "y":"r", "r": {"id":"mnopqrstuvwxyz123456"}}
bencoded = d1:rd2:id20:mnopqrstuvwxyz123456e1:t2:aa1:y1:re
Find_node#
KRPC プロトコルにおいて、「find_node」は指定されたノード ID の連絡先情報を検索するために使用されます。「find_node」クエリには 2 つのパラメータが含まれます:「ID」はリクエスト元ノードの ID を示し、「target」はクエリ対象ノードの ID を示します。
ノードが「find_node」クエリメッセージを受信すると、自身のルーティングテーブルとクエリ対象ノードの ID に基づいて応答内容を決定します。ノードがクエリ対象ノードの連絡先情報を持っている場合、それを文字列形式のコンパクトノード情報として応答メッセージの「nodes」キーに追加して返します。ノードがクエリ対象ノードの連絡先情報を持っていない場合、クエリ対象ノード ID に最も近い K(通常は 8)個のノードのコンパクトノード情報を自身のルーティングテーブルから返します。
「find_node」クエリメッセージを使用し、応答を受信することで、ノードは他のノードの連絡先情報を知り、ネットワークトポロジーの構築と維持を行うことができます。
arguments: {"id" : "<querying nodes id>", "target" : "<id of target node>"}
response: {"id" : "<queried nodes id>", "nodes" : "<compact node info>"}
サンプルデータパケット
find_node Query = {"t":"aa", "y":"q", "q":"find_node", "a": {"id":"abcdefghij0123456789", "target":"mnopqrstuvwxyz123456"}}
bencoded = d1:ad2:id20:abcdefghij01234567896:target20:mnopqrstuvwxyz123456e1:q9:find_node1:t2:aa1:y1:qe
Response = {"t":"aa", "y":"r", "r": {"id":"0123456789abcdefghij", "nodes": "def456..."}}
bencoded = d1:rd2:id20:0123456789abcdefghij5:nodes9:def456...e1:t2:aa1:y1:re
Get_peers#
KRPC プロトコルにおいて、「get_peers」は特定のトレントの infohash に関連付けられたノードのリストを取得するために使用されます。「get_peers」クエリには 2 つのパラメータが含まれます:「ID」はリクエスト元ノードの ID を示し、「info_hash」はクエリ対象のトレントファイルの infohash を示します。
ノードが「get_peers」クエリメッセージを受信すると、自身が保存している情報に基づいて応答内容を決定します。ノードがクエリ対象のトレントの infohash に関連付けられた peer を持っている場合、それらの peer のコンパクト形式情報を文字列として応答メッセージの「values」キーに追加して返します。各文字列は 1 つの peer のコンパクト形式情報を示します。
ノードがクエリ対象のトレントの infohash に関連付けられた peer を持っていない場合、infohash に最も近い K(通常は 8)個のノードのコンパクトノード情報を自身のルーティングテーブルから返します。この場合、応答メッセージには「token」キーが含まれ、その値は短いバイナリ文字列です。この TOKEN キーは、将来の「announce_peer」クエリの必須パラメータとして使用できます。
「get_peers」クエリメッセージを使用し、応答を受信することで、ノードは特定のトレントの infohash に関連付けられたノードのリストを知り、適切な接続と相互作用を行うことができます。
arguments: {"id" : "<querying nodes id>", "info_hash" : "<20-byte infohash of target torrent>"}
response: {"id" : "<queried nodes id>", "token" :"<opaque write token>", "values" : ["<peer 1 info string>", "<peer 2 info string>"]}
or: {"id" : "<queried nodes id>", "token" :"<opaque write token>", "nodes" : "<compact node info>"}
サンプルデータパケット:
get_peers Query = {"t":"aa", "y":"q", "q":"get_peers", "a": {"id":"abcdefghij0123456789", "info_hash":"mnopqrstuvwxyz123456"}}
bencoded = d1:ad2:id20:abcdefghij01234567899:info_hash20:mnopqrstuvwxyz123456e1:q9:get_peers1:t2:aa1:y1:qe
Response with peers = {"t":"aa", "y":"r", "r": {"id":"abcdefghij0123456789", "token":"aoeusnth", "values": ["axje.u", "idhtnm"]}}
bencoded = d1:rd2:id20:abcdefghij01234567895:token8:aoeusnth6:valuesl6:axje.u6:idhtnmee1:t2:aa1:y1:re
Response with closest nodes = {"t":"aa", "y":"r", "r": {"id":"abcdefghij0123456789", "token":"aoeusnth", "nodes": "def456..."}}
bencoded = d1:rd2:id20:abcdefghij01234567895:nodes9:def456...5:token8:aoeusnthe1:t2:aa1:y1:re
Announce_peer#
制御クエリノードの対等者がポートでシードをダウンロードしていることを発表します。announce_peer には 4 つのパラメータがあります:「id」はクエリノードのノード ID を含み、「info_hash」はシードの情報ハッシュを含み、「port」は整数としてのポートを含み、前の get_peers クエリに対する応答で受け取った「token」を含みます。
ノードは他のノードに特定のシードファイルの peer リストをクエリし、announce_peer リクエストを通じて他のノードにその特定のシードファイルをダウンロードしていることを通知し、ポート番号と以前受け取ったトークンを提供してその身元を確認します。問い合わせられたノードは、トークンが同じ IP アドレスからのものであることを確認し、クエリノードの IP アドレスとポート番号をその peer 連絡情報の対応する infohash の下に保存する必要があります。これにより、他のノードはそのノードに接続してその特定のシードファイルをダウンロードできるようになります。
オプションのパラメータ implied_port は、0 または 1 の値を取ります。このパラメータが存在し、非ゼロの場合、port パラメータは無視され、UDP パケットのソースポートが peer のポートとして使用されます。これは、NAT の背後にいる peer にとって便利です。なぜなら、彼らは自分の外部ポートを知らない可能性があるからです。また、uTP プロトコルをサポートしており、DHT ポートと同じポートで受信接続を受け入れます。
arguments: {"id" : "<querying nodes id>",
"implied_port": <0 or 1>,
"info_hash" : "<20-byte infohash of target torrent>",
"port" : <port number>,
"token" : "<opaque token>"}
response: {"id" : "<queried nodes id>"}
サンプルデータパケット:
announce_peers Query = {"t":"aa", "y":"q", "q":"announce_peer", "a": {"id":"abcdefghij0123456789", "implied_port": 1, "info_hash":"mnopqrstuvwxyz123456", "port": 6881, "token": "aoeusnth"}}
bencoded = d1:ad2:id20:abcdefghij012345678912:implied_porti1e9:info_hash20:mnopqrstuvwxyz1234564:porti6881e5:token8:aoeusnthe1:q13:announce_peer1:t2:aa1:y1:qe
Response = {"t":"aa", "y":"r", "r": {"id":"mnopqrstuvwxyz123456"}}
bencoded = d1:rd2:id20:mnopqrstuvwxyz123456e1:t2:aa1:y1:re
参考リンク#
http://bittorrent.org/beps/bep_0005.html
https://hazelcast.com/glossary/distributed-hash-table/
https://en.wikipedia.org/wiki/Distributed_hash_table