[译]BitTorrent协议规范
BitTorrent 是一個(gè)用于文件分發(fā)的協(xié)議。它通過(guò) URL 來(lái)標(biāo)識(shí)內(nèi)容,其設(shè)計(jì)使其可以與 Web 無(wú)縫集成。BitTorrent 相對(duì)于一般 HTTP 的優(yōu)勢(shì)在于,當(dāng)相同文件的多個(gè)下載并行進(jìn)行時(shí),下載者之間還可以互傳數(shù)據(jù),這就使得文件源在僅增加少量負(fù)載的情況下支持?jǐn)?shù)量眾多的下載成為可能。
BitTorrent 文件分發(fā)由這些實(shí)體組成
- 一個(gè)普通的 Web 服務(wù)器
- 一個(gè)靜態(tài)的 ‘metainfo’ 文件
- 一個(gè) BitTorrent tracker
- 一個(gè) ‘原始的’ 下載者
- 終端用戶 Web 瀏覽器
- 終端用戶下載者
理想情況下,單個(gè)文件有多個(gè)終端用戶在下載。
要提供 BitTorrent 下載服務(wù),主機(jī)需要執(zhí)行如下步驟
注:
總結(jié)一下,提供 BitTorrent 文件下載服務(wù),需要如下步驟:
1. 啟動(dòng) Tracker 服務(wù)。
2. .torrent 文件的生成和發(fā)布:這包括根據(jù)要發(fā)布的文件本身的內(nèi)容和 Tracker 服務(wù)器的 URL 生成 metainfo 文件,即 .torrent 文件;將 .torrent 文件發(fā)布到 Web 服務(wù)器上,提供對(duì)該 .torrent 文件的下載服務(wù);將 .torrent 文件的鏈接盡可能的傳播出去。
3. 啟動(dòng)提供文件的下載源。
真正用于實(shí)際的文件下載的組件是 Tracker 服務(wù)器,和文件的下載源節(jié)點(diǎn)。
要啟動(dòng)下載,用戶需執(zhí)行如下步驟
注:
總結(jié)一下,BitTorrent 文件下載有兩個(gè)過(guò)程:一是下載 .torrent 文件;二是使用 BitTorrent 客戶端下載文件,數(shù)據(jù)的來(lái)源將是網(wǎng)絡(luò)中包含文件數(shù)據(jù)的其它節(jié)點(diǎn)。
B編碼
字符串表示為,十進(jìn)制的長(zhǎng)度前綴,后跟冒號(hào)和字符串本身。比 4:spam 表示 spam。
整數(shù)表示為,i 后跟十進(jìn)制數(shù)字,然后是一個(gè) e 字符表示結(jié)束。比如 i3e 表示 3,i-3e 表示 -3。整數(shù)沒(méi)有大小限制。i-0e 是無(wú)效的。所有以 0 為前導(dǎo)的編碼,比如 i03e,都是無(wú)效的,除了 i0e,而它當(dāng)然表示 0。
列表被編碼為,l 后跟它們的元素(也是 B 編碼的),然后是 e 字符表示結(jié)束。比如 l4:spam4:eggse 表示 ['spam', 'eggs']。
字典編碼為,d 后交替地跟著鍵和它們的對(duì)應(yīng)值,然后是 e 字符表示結(jié)束。比如 d3:cow3:moo4:spam4:eggse 表示 {'cow': 'moo', 'spam': 'eggs'},而 d4:spaml1:a1:bee 表示 {'spam': ['a', 'b']}。鍵必須是字符串,且順序排列(以原始串排序,而不是字母順序)。
注:
BitTorrent 協(xié)議發(fā)明了一種編碼方式,即 B 編碼。B 編碼編碼 4 種數(shù)據(jù)結(jié)構(gòu)或?qū)ο?#xff0c;即字符串,整數(shù),列表,和字典(即映射)。基本數(shù)據(jù)類型是字符串和數(shù)字,列表和字典則是兩種最常用的容器。
metainfo 文件
Metainfo 文件(即 .torrent 文件)是 B 編碼的字典,它具有如下的鍵:
announce
- Tracker 的 URL。
info
- 它映射到一個(gè)字典,下面會(huì)描述它的鍵。
一個(gè) .torrent 文件中所有包含文本的字符串必須以 UTF-8 編碼。
info 字典
name 鍵映射為一個(gè) UTF-8 編碼的字符串,它建議的保存文件(或字典)時(shí)所采用的名字。這純粹是建議性的。
piece length 映射為文件分割后每個(gè)片的字節(jié)數(shù)。為了便于數(shù)據(jù)傳輸,文件被分割為固定大小的片,它們具有相同的長(zhǎng)度,除了最后的文件片可能由于截?cái)喽煌狻iece length 幾乎總是 2 的冪,最常見(jiàn)的是 2^18 = 256K(3.2 版之前的 BitTorrent 版本使用 2^20 = 1M 作為默認(rèn)值)。
pieces 映射為長(zhǎng)度是 20 的整數(shù)倍的字符串。它將被細(xì)分為長(zhǎng)度為 20 的字符串,其中每個(gè)都是對(duì)應(yīng) index 處的片的 SHA1 hash 值。
還有一個(gè)鍵 length 或鍵 files,但不會(huì)都有或都沒(méi)有。如果是 length,表示下載是單個(gè)文件,否則表示下載是目錄結(jié)構(gòu)中的一系列文件。
在單個(gè)文件的情況中,length 映射為文件的字節(jié)長(zhǎng)度。
出于其他鍵的目的,多文件的情況僅被看作將 files 列表中出現(xiàn)的文件順序連接形成的單個(gè)文件。文件列表是 files 映射的值,它是包含如下鍵的字典的列表:
length - 文件的長(zhǎng)度,以字節(jié)為單位。
path - UTF-8 編碼的字符串的列表,對(duì)應(yīng)于子目錄的名字,它的最后部分是實(shí)際的文件名(長(zhǎng)度為 0 的列表是錯(cuò)誤情況)。
在單個(gè)文件的情況中,name 鍵是文件名,在多文件的情況中,它是目錄名。
注:
用 JSON 來(lái)表示 .torrent 文件使我們可以對(duì)這種文件格式的結(jié)構(gòu)有更清晰的認(rèn)識(shí)。單個(gè)文件的 .torrent 文件結(jié)構(gòu)如下:
多個(gè)文件的 .torrent 文件結(jié)構(gòu)如下:
{"announce":"xxxx","info":{"name":"xxxx","piece length":"xxxx","pieces":"xxxx","files":[{"length":"xxxx","path":"xxxx"},{"length":"xxxx","path":"xxxx"},{"length":"xxxx","path":"xxxx"}]} }trackers
Tracker GET 請(qǐng)求具有如下的鍵:
info_hash
- 是 metainfo 文件中 info 值的 B 編碼形式的 20 字節(jié) SHA1 哈希。這個(gè)值幾乎肯定必須被轉(zhuǎn)義。
- 注意,這是 metainfo 文件的一個(gè)子串。info-hash 必須是 .torrent 文件中找到的編碼形式的哈希,這與對(duì) metainfo 文件進(jìn)行 B 解碼,提取 info 字典并對(duì)其進(jìn)行編碼相同,當(dāng)且僅當(dāng) B 解碼器完全驗(yàn)證輸入之后(比如鍵的順序,沒(méi)有前導(dǎo) 0)。相反,這意味著客戶端必須拒絕無(wú)效的 metainfo 文件或直接提取子串。它們不得對(duì)無(wú)效數(shù)據(jù)執(zhí)行解碼-編碼循環(huán)。
peer_id
- 一個(gè)下載器用作其 ID 的長(zhǎng)度為 20 的字符串。每個(gè)下載器在新下載開(kāi)始時(shí)隨機(jī)生成它自己的 ID。這個(gè)值也幾乎肯定必須被轉(zhuǎn)義。
ip
- 一個(gè)可選的參數(shù),它給出了這個(gè) Peer 的 IP 地址(或域名)。如果它與 tracker 位于相同機(jī)器的話,通常被用作源。
port
- 當(dāng)前 Peer 監(jiān)聽(tīng)的端口號(hào)。下載器的常見(jiàn)行為是,嘗試監(jiān)聽(tīng)端口 6881,但如果那個(gè)端口被占用則嘗試端口 6882,然后是 6883,等等,直到 6889 之后放棄。
uploaded
- 到目前為止上傳的數(shù)據(jù)量,以十進(jìn)制 ascii 編碼。
downloaded
- 到目前為止下載的數(shù)據(jù)量,以十進(jìn)制 ascii 編碼。
left
- 當(dāng)前 Peer 仍然需要下載的字節(jié)數(shù),以十進(jìn)制 ascii 編碼。注意,這個(gè)值不能用文件的長(zhǎng)度和已經(jīng)下載的數(shù)據(jù)量來(lái)計(jì)算,因?yàn)樗赡苁腔謴?fù)的下載,而且,也有可能下載回來(lái)的一些數(shù)據(jù)在完整性檢查時(shí)失敗,而不得不重新下載。
event
- 這是一個(gè)可選的鍵,它映射為 started,completed,或 stopped(或empty,與沒(méi)有這個(gè)鍵一樣)。如果不存在,則這是定期進(jìn)行的公告請(qǐng)求之一。使用 started 的公告在下載首次啟動(dòng)時(shí)發(fā)送,使用 completed 的將在下載完成時(shí)發(fā)送。如果啟動(dòng)時(shí)文件已經(jīng)完成則不會(huì)發(fā)送 completed。當(dāng)停止下載時(shí)下載器發(fā)送一個(gè)使用 stopped 的公告。
注:看了半天也沒(méi)看懂,到底如何計(jì)算 info_hash。ip 字段的用作源到底是什么意思?是用作數(shù)據(jù)源么?
由此可見(jiàn),BitTorrent 客戶端向 Tracker 服務(wù)器發(fā)送的請(qǐng)求數(shù)據(jù),如果以 JSON 格式表示的話,看起來(lái)可能像下面這樣:
但具體是什么樣的結(jié)構(gòu)化數(shù)據(jù)表示格式,目前還看不出來(lái)。
Tracker 的響應(yīng)也是 B 編碼的字典。如果 Tracker 的響應(yīng)包含 failure reason 鍵,則它是解釋查詢失敗的原因的可讀字符串,且不會(huì)再有其它鍵。否則,它必須包含兩個(gè)鍵:interval,它是下載器在兩次正常的重新請(qǐng)求之間應(yīng)該等待的秒數(shù),和 peers。peers 是字典的列表,即網(wǎng)絡(luò)中的 Peer 信息,列表中的每一項(xiàng)都包含鍵 peer id,ip 和 port,它們分別是 Peer 為自己選擇的 ID,字符串形式的 IP 地址或域名,和端口號(hào)。注意,如果事件發(fā)生或它們需要更多 Peers,則下載器可能在非調(diào)度時(shí)間進(jìn)行重請(qǐng)求。
更常見(jiàn)的是,Trackers 返回 Peer 列表的兼容表示,參見(jiàn) BEP 23。
注:
Tracker 服務(wù)器的響應(yīng)數(shù)據(jù)的格式,說(shuō)明的還是比較清晰的,即 B 編碼的字典。響應(yīng)數(shù)據(jù)的格式分為兩種,分別是請(qǐng)求失敗時(shí)的響應(yīng),和請(qǐng)求成功時(shí)的響應(yīng)。請(qǐng)求失敗時(shí)的響應(yīng)格式,如果用 JSON 格式來(lái)表示的話,大概看起來(lái)像這樣:
請(qǐng)求成功時(shí),響應(yīng)中包含了正在下載相同資源的其它節(jié)點(diǎn)的信息,如果用 JSON 格式來(lái)表示這種響應(yīng)的話,大概看起來(lái)像這樣:
{"interval":12345,"peers":[{"peer id":"xxxxxx","ip":"xxxxxx","port":1234},{"peer id":"xxxxxx","ip":"xxxxxx","port":1234},{"peer id":"xxxxxx","ip":"xxxxxx","port":1234}] }如果你想對(duì) metainfo 文件或 Tracker 查詢做擴(kuò)展,請(qǐng)與 Bram Cohen 合作來(lái)確認(rèn)所有擴(kuò)展的兼容性。
通過(guò) UDP Tracker 協(xié)議通告也很常見(jiàn)。
注:
由上面的內(nèi)容不難理解,Tracker 服務(wù)器 BitTorrent 網(wǎng)絡(luò)中的中心化服務(wù)器,主要用于管理網(wǎng)絡(luò)中的節(jié)點(diǎn)。
Peer 協(xié)議
BitTorrent 的 Peer 協(xié)議操作基于 TCP 或 uTP。
Peer 連接是對(duì)稱的。消息在兩個(gè)方向上的發(fā)送看起來(lái)是一樣的,數(shù)據(jù)可在任一方向上流動(dòng)。
Peer 協(xié)議通過(guò) metainfo 文件中所描述的索引引用文件的片段,索引從零開(kāi)始。當(dāng) Peer 完成一個(gè)片的下載,并檢查了哈希值的匹配性,它將向它所有的 Peers 通告它具有該片。
連接在任一端包含兩個(gè)狀態(tài)位:阻塞或不阻塞,以及是否感興趣。(注:即與對(duì)端的數(shù)據(jù)通道是否暢通,以及對(duì)端是否具有自己需要的數(shù)據(jù)。)阻塞是一種通知,在阻塞解除之前將不會(huì)發(fā)送任何數(shù)據(jù)。阻塞背后的原因和常見(jiàn)技術(shù)將在本文后面解釋。
數(shù)據(jù)傳輸發(fā)生在一端需要數(shù)據(jù)而另一端沒(méi)有阻塞的時(shí)候。感興趣狀態(tài)必須隨時(shí)保持更新 - 當(dāng)下載者無(wú)需在對(duì)端處于非阻塞狀態(tài)的話,就向?qū)Χ苏?qǐng)求數(shù)據(jù)的時(shí)候,它們必須表達(dá)缺乏興趣,盡管正在被阻塞。正確實(shí)現(xiàn)這一點(diǎn)很棘手,但是下載者可以知道哪些 Peers 會(huì)在解除阻塞時(shí)立即開(kāi)始下載。
連接開(kāi)始時(shí)處于阻塞且不感興趣狀態(tài)。
當(dāng)傳輸數(shù)據(jù)時(shí),下載者應(yīng)該一次在隊(duì)列中保持多個(gè)片的請(qǐng)求以獲得較好的 TCP 性能(這被稱為 ‘pipelining’)。另一方面,無(wú)法立即寫(xiě)入 TCP 緩沖區(qū)的請(qǐng)求應(yīng)該放進(jìn)內(nèi)存隊(duì)列中,而不是應(yīng)用級(jí)的網(wǎng)絡(luò)緩沖區(qū)中,以使它們?cè)谧枞l(fā)生時(shí)可以被扔掉。(注:內(nèi)存緩沖區(qū)和應(yīng)用級(jí)緩沖區(qū)到底分別指什么?)
Peer 線協(xié)議由握手消息及其后永不停止的長(zhǎng)度前綴消息流組成。握手消息由字符 9 (十進(jìn)制)及其后面的字符串 BitTorrent protocol 構(gòu)成。前導(dǎo)字符是長(zhǎng)度前綴,希望其他新協(xié)議可以做同樣的事情,因此可以在很小的程度上相互區(qū)分。
協(xié)議中后續(xù)發(fā)送的整數(shù)都以四字節(jié)大尾端編碼。
固定頭部之后是 8 個(gè)保留字節(jié),在當(dāng)前所有的實(shí)現(xiàn)中它們都是 0。如果你想使用這些字節(jié)擴(kuò)展協(xié)議,請(qǐng)與 Bram Cohen 合作,以確保所有擴(kuò)展可以兼容地完成。
接下來(lái)是 metainfo 文件中 info 值的 B 編碼形式的 20 字節(jié) SHA1 哈希。(這個(gè)值與向 Tracker 服務(wù)器公告的 info_hash 相同,只是在這里它是原始的而不是在這里引用的)。如果兩端沒(méi)有發(fā)送相同的值,則切斷連接。一個(gè)可能的例外是,如果下載者想要通過(guò)單個(gè)端口執(zhí)行多個(gè)下載,則他們可能會(huì)等待傳入連接首先提供下載哈希,并且如果它在列表中的話則以相同的哈希響應(yīng)。
下載哈希后面的是 20 字節(jié)的 Peer ID,該 Peer ID 由 Tracker 請(qǐng)求報(bào)告,并包含在 Tracker 響應(yīng)的 Peer 列表中。如果接收端的 Peer ID 與發(fā)起端的期待不匹配,則它切斷連接。
這就是握手消息,接下來(lái)是長(zhǎng)度前綴和消息的交替流。長(zhǎng)度為 0 的消息是 keepalives,它們會(huì)被忽略。Keepalives 通常每 2 分鐘發(fā)送一次,但是注意,在預(yù)期數(shù)據(jù)時(shí)可以更快地完成超時(shí)。
Peer 消息
所有的非 keepalive 消息以一個(gè)標(biāo)識(shí)消息類型的字節(jié)開(kāi)始。
可能的值為:
- 0 - choke
- 1 - unchoke
- 2 - interested
- 3 - not interested
- 4 - have
- 5 - bitfield
- 6 - request
- 7 - piece
- 8 - cancel
choke,unchoke,interested,和 not interested 沒(méi)有載荷。
‘bitfield’ 只作為第一條消息發(fā)送。它的載荷是一個(gè)位域,其中下載器已經(jīng)發(fā)送的每個(gè)索引設(shè)置為 1,其它設(shè)置為 0。沒(méi)有任何東西的下載器可以跳過(guò) bitfield 消息。位域的第一個(gè)字節(jié)從高位到低位分別對(duì)應(yīng)于索引 0 - 7。接下來(lái)的一個(gè)是 8 - 15,等等。最后一個(gè)字節(jié)中多余的位設(shè)置為 0。
have 消息的載荷是一個(gè)數(shù)字,是下載器剛剛完成并檢查了哈希值的索引。
request 消息包含索引,起始位置,和長(zhǎng)度。后兩個(gè)是字節(jié)偏移量。長(zhǎng)度通常是 2 的冪,除非它被文件尾截?cái)唷K挟?dāng)前的實(shí)現(xiàn)使用 2^14 (16 kiB),當(dāng)請(qǐng)求比這個(gè)值更大數(shù)量的數(shù)據(jù)時(shí),連接關(guān)閉。
cancel 消息具有與 request 消息相同的載荷。它們通常僅在下載結(jié)束時(shí)發(fā)送,稱為“結(jié)束游戲模式”。當(dāng)下載快結(jié)束時(shí),最后一些數(shù)據(jù)片傾向于從單個(gè)調(diào)制解調(diào)器線上下載,這需要很長(zhǎng)時(shí)間。為了確保最后的數(shù)據(jù)片快速地到達(dá),一旦特定下載器還沒(méi)有下載到的所有數(shù)據(jù)片的請(qǐng)求都處于掛起狀態(tài),則它向當(dāng)前正從其下載的每個(gè) Peer 請(qǐng)求所有的數(shù)據(jù)。為了防止這種情況變得特別低效,每次數(shù)據(jù)片到達(dá)時(shí)它都向其它 Peer 發(fā)送 cancel。
piece 消息包含索引,起始位置,和數(shù)據(jù)片。注意,它們隱式地與請(qǐng)求消息相關(guān)聯(lián)。如果 choke 和 unchoke 消息快速連續(xù)地發(fā)送和/或傳輸速度非常慢的話,可能會(huì)有未預(yù)期的數(shù)據(jù)片到達(dá)。
下載器通常以隨機(jī)順序下載數(shù)據(jù)片,這樣可以很好地防止它們擁有任何 Peers 的片的嚴(yán)格子集或超集。
Choking 有幾個(gè)原因。當(dāng)一次性通過(guò)多個(gè)連接發(fā)送時(shí),TCP 擁塞控制的表現(xiàn)很差。而且,choking 讓每個(gè) Peer 使用 tit-for-tat-ish 算法以確保它們獲得一致的下載速率。
下面描述的 choking 算法正是當(dāng)前部署的。所有新算法在完全由他們自己組成的網(wǎng)絡(luò)中,以及在主要由這個(gè)算法組成的網(wǎng)絡(luò)中,都能很好地工作是非常重要的。
一個(gè)好的 choking 算法應(yīng)該滿足一些標(biāo)準(zhǔn)。它應(yīng)該限制并發(fā)上傳的數(shù)量以獲得良好的 TCP 性能。它應(yīng)該避免快速地 choking 和 unchoking,這被稱為 抖動(dòng)。它應(yīng)該回應(yīng)讓它下載的 Peer。最后,它應(yīng)該偶爾嘗試使用未使用的連接,以確定它們是否可能比當(dāng)前使用的更好,這稱為樂(lè)觀的 unchoking。
當(dāng)前部署的 choking 算法通過(guò)僅改變每隔 10 秒鐘 choked 一次的 Peer 避免了抖動(dòng)。它通過(guò)解除對(duì)其具有最佳下載速率并且感興趣的四個(gè) peers 來(lái)實(shí)現(xiàn)往復(fù)和上傳數(shù)量的限制。擁有更高上傳率但不感興趣的 peer 會(huì)被 unchoked,如果它們變得被感興趣則最糟糕的上傳者被 choked。如果下載者擁有一個(gè)完整的文件,它使用它的上傳速率而不是它的下載速率來(lái)決定誰(shuí) unchoke。
對(duì)于樂(lè)觀的 unchoking,在任何時(shí)間都有一個(gè)單獨(dú)的 peer,它處于 unchoked 狀態(tài)而無(wú)論它的上傳速率是多少(如果感興趣,它被作為四個(gè)允許的下載者中的一個(gè))。哪個(gè) peer 是樂(lè)觀的 unchoked 每隔 30 秒輪轉(zhuǎn)一次。為了給他們一個(gè)很好的機(jī)會(huì)獲得一個(gè)完整的片段上傳,新的連接的開(kāi)始時(shí)間是當(dāng)前樂(lè)觀的unchoke的三倍,與旋轉(zhuǎn)中的其他任何地方一樣。
資源
- BitTorrent 經(jīng)濟(jì)論文 列出了一些客戶端應(yīng)該實(shí)現(xiàn)的用于優(yōu)化性能的請(qǐng)求和 choking 算法。
- 當(dāng)開(kāi)發(fā)新的實(shí)現(xiàn)時(shí),Wireshark 協(xié)議分析器和它的 針對(duì) bittorrent 的解剖器 在調(diào)試和對(duì)比已有的實(shí)現(xiàn)時(shí)可能很有用。
版權(quán)
本文檔已置于公共領(lǐng)域。
原文鏈接。
總結(jié)
以上是生活随笔為你收集整理的[译]BitTorrent协议规范的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: mupdf-android-viewer
- 下一篇: 搭建 LEGO EV3 的 PyChar