BitTorrent 中的 Extension Protocol 是一種用於擴展和增強 BitTorrent 下載協議功能的機制。它允許客戶端通過發送和響應與標準協議不同的消息來進行通信。這些消息可以實現新的功能,如 DHT(分佈式哈希表)交換、PEX(對等交換)以及支持更高級別的加密。
Extension Protocol 在 BitTorrent 協議中起著至關重要的作用,因為它允許客戶端進行更有效和更可靠的數據交換,並提供了更好的隱私和安全性。例如,DHT 支持無需 Tracker 即可查找其他已連接到網絡的對等方;而 PEX 則允許客戶端直接與其他客戶端通信,從而使數據交換更加穩定和可靠。
協議簡介#
這個協議的目的是為 BitTorrent 協議的擴展提供一個簡單而輕量的傳輸方式。支持這個協議可以很容易地添加新的擴展功能,而不會干擾標準的 BitTorrent 協議或不支持你想要添加的擴展功能的客戶端。換句話說,這個協議提供了一種靈活、可擴展的機制,使得開發者能夠在不影響其他用戶的情況下,對 BitTorrent 協議進行個性化的擴展和定制。
在 BitTorrent 協議中,為了向其他支持的客戶端傳播自己,會使用保留字節中的一個字節。其中,用於擴展協議的字節是從右邊開始數的第 20 個(從 0 開始計數)。因此,可以通過表達式(reserved_byte [5] & 0x10)來檢查客戶端是否支持擴展消息傳輸。這個表達式的含義是,將保留字節中的第 5 個字節與 0x10 進行按位與運算,如果結果不為 0,則表示客戶端支持擴展消息。
建立對協議的支持後,客戶端應該支持 1 條新消息:
name | id |
---|---|
extended | 20 |
這個消息與其他 BitTorrent 消息一樣,以 4 字節的長度前綴和一個單字節的標識符(在本例中為 20)發送。在消息有效負載的開頭,有一個單字節的消息標識符。該標識符可以引用不同的擴展消息,但只指定了一個 ID,即 0 。如果 ID 是 0,則表示這個消息是一個握手消息,下面會進行描述。一般擴展消息的佈局如下所示(包括 BitTorrent 協議使用的消息頭):
size | description |
---|---|
uint32_t | 長度前綴。指定整個消息的字節數。(大端序 1) |
uint8_t | bittorrent 消息 ID, = 20 |
uint8_t | 擴展消息 ID 。 0 = 握手,>0 = 握手指定的擴展消息。 |
握手消息#
握手消息的有效負載是一個 bencoded 字典。字典中的所有項都是可選的。客戶端應該忽略字典中未知的名稱。字典中的所有部分都區分大小寫。字典中定義了以下項目:
name | description |
---|---|
m | 支持的擴展消息字典將每個擴展消息的名稱映射到一個擴展消息 ID 。對這些 ID 唯一的要求是,沒有兩個擴展消息可以共享同一個 ID 。將擴展號設置為零意味著該擴展未被支持 / 禁用。客戶端應忽略其不識別的任何擴展名。擴展消息 ID 是用於將擴展消息發送到發送此握手的同行方的 ID 。也就是說,這些 ID 是特定於此特定對等體的本地 ID 。 |
這裡列出了一些實現可能選擇支持的項目:
name | description |
---|---|
p | 本地 TCP 監聽端口。允許每一方了解另一方的 TCP 端口號。請注意,接收連接的一方不需要發送此擴展消息,因為其端口號已知。 |
v | 客戶端名稱和版本(作為 UTF-8 字符串)。這是比依賴對等方 ID 編碼更可靠的識別客戶端的方法。 |
yourip | 一個包含 IP 地址緊湊表示的字符串,用於表示此節點看到您的 IP 地址。也就是說,這是接收方的外部 IP 地址(不包括端口)。它可以是 IPv4(4 個字節)或 IPv6(16 個字節)地址。 |
ipv6 | 如果此節點具有 IPv6 接口,則這是該地址的緊湊表示(16 個字節)。客戶端可能更喜歡通過 IPv6 地址進行連接。 |
ipv4 | 如果此節點具有 IPv4 接口,則這是該地址的緊湊表示(4 個字節)。客戶端可能更喜歡通過 IPv4 地址進行連接。 |
reqq | 一個整數,表示此客戶端支持的未完成請求消息數量,而不會丟失任何消息。在 libtorrent 中,默認值為 250 。 |
握手字典還可以包括擴展的握手信息,例如支持加密頭部或任何可能的內容。
以下是握手消息負載的示例內容:
Dictionary | |
---|---|
m | Dictionary / LT_metadata······1 / ut_pex··················2 |
p | 6881 |
v | “µTorrent 1.2” |
並以編碼形式:
d1:md11:LT_metadatai1e6:µT_PEXi2ee1:pi6881e1:v13:\xc2\xb5Torrent 1.2e
為了避免擴展名不小心發生衝突,應該在擴展名前加上一個或兩個字符的代碼,以標識引入擴展的客戶端。這適用於擴展消息的名稱以及放在頂層字典中的任何其他信息。除非由本規範定義,否則所有一字節和兩字節標識符均無效。
此消息應在標準 bittorrent 握手後立即發送到支持此擴展協議的任何節點。在連接的整個周期內,可以多次發送握手消息,發送方不應斷開連接。實現可以選擇忽略後續的握手消息(或其部分)。
隨後的握手消息可用於在不重新啟動連接的情況下啟用 / 禁用擴展。如果節點持在運行時更改擴展,則應注意 m 字典是累加的。它包含擴展列表的實際更改就足夠了。要在運行時禁用對 LT_metadata 的支持,而不影響任何其他擴展,應發送此消息:d11 。如上所述,值 0 用於關閉擴展。
必須為每個節點存儲擴展 ID,因為每個節點可能具有相同擴展的不同 ID 。
該規範故意沒有指定任何擴展,例如節點交換或元數據交換。該協議僅是 bittorrent 協議實際擴展的傳輸方式,上面的示例中命名的擴展(例如 p)只是可能擴展的示例。
合理的依據#
為了避免使用全局消息 ID 註冊表,擴展消息的 ID 在握手過程中被定義的原因是為了確保擴展消息名稱具有唯一性,這樣做比使用全局註冊表更容易。慣例是在擴展消息名稱上使用兩個字母前綴,該前綴將標識最先實現該擴展消息的客戶端。例如,LT_metadata 由 libtorrent 實現,因此它具有 LT 前綴。
如果支持擴展的客戶端可以決定其接收到的消息將具有哪些數字,那麼這些數字就是該客戶端內的常量。也就是說,它們可以用於 switch 語句。對於另一端來說,存儲一個包含每個消息期望的 ID 的數組並在每次發送擴展消息時使用該數組進行查找非常容易。
使用字典而不是使用(用於隱式分配索引號擴展的)數組的原因是,如果客戶端想要禁用某些擴展,ID 號碼將會改變,它將無法使用常量(因此,不能在 switch 中使用它們)。如果消息 ID 直接映射到 BitTorrent 消息 ID,則還可以將握手中的擴展映射到具有固定消息 ID 的現有擴展。
使用單個字節作為擴展消息標識符的原因是為了遵循 BitTorrent 規範的單個字節消息標識符。這被認為足夠了。它不會限制擴展的總數,只會限制同時使用的擴展數。
使用單個字節標識符作為標準化握手標識符的原因是:1)主線 DHT 使用單個字節標識符;2)節省帶寬。更長消息的唯一優點是使協議對人類更易讀,但是 BT 協議並不是設計成人類可讀的協議,所以為什麼要費心呢。
總的來說,Extension Protocol 是 BitTorrent 協議的一個重要組成部分,它為用戶提供了更多的功能和更好的使用體驗。