uTorrent transport protocol
uTorrent Transport Protocol
- 設(shè)計人員
- 產(chǎn)生背景
- 總體介紹
- 頭部格式
- type
- version
- connection_id
- timestamp_microseconds
- timestamp_difference_microseconds
- wnd_size
- seq_nr
- ack_nr
- extension
- SELECTIVE ACK
- 建立連接
- 包丟失
- 超時
- 包大小
- 擁塞控制
設(shè)計人員
uTorrent傳輸協(xié)議由Ludvig Strigeus,Greg Hazel,Stanislav Shalunov,Arvid Norberg和Bram Cohen設(shè)計。
產(chǎn)生背景
uTP的起源動機(jī)是讓BitTorrent客戶端不會中斷互聯(lián)網(wǎng)連接,同時仍然充分利用未使用的帶寬。
問題是DSL和電纜調(diào)制解調(diào)器通常具有與其最大發(fā)送速率不成比例的發(fā)送緩沖器,其可以容納幾秒鐘的數(shù)據(jù)包。BitTorrent流量通常是后臺傳輸,應(yīng)該比檢查電子郵件,電話和瀏覽網(wǎng)頁的優(yōu)先級低,但是當(dāng)使用常規(guī)TCP連接時,BitTorrent會快速填滿發(fā)送緩沖區(qū)(多并發(fā)連接),為所有交互式流量增加多秒延遲。
BitTorrent使用多個TCP連接這一事實(shí)使其在與其他服務(wù)競爭帶寬時具有不公平的優(yōu)勢,這夸大了BitTorrent填充上傳管道的影響。這是因為TCP在連接中均勻分配可用帶寬,并且一個應(yīng)用程序使用的連接越多,它所獲得的帶寬份額就越大。
這個問題的傳統(tǒng)解決方案是將BitTorrent客戶端的上傳速率限制在上行鏈路容量的80%。80%為交互式交通留下了一些空間。
該解決方案的主要缺點(diǎn)是:
uTP通過使用調(diào)制解調(diào)器隊列大小作為其發(fā)送速率的控制器來解決此問題。當(dāng)隊列變得太大時,它會限制回來。
這使得它可以在沒有競爭的情況下利用完整的上傳容量,并且當(dāng)有大量交互流量時,它可以讓它減少到幾乎沒有。
總體介紹
本文檔假設(shè)您了解TCP和基于窗口的擁塞控制的工作原理。
uTP是一種在UDP之上分層的傳輸協(xié)議。因此,它必須(并且有能力)實(shí)現(xiàn)自己的擁塞控制。
與TCP相比的主要區(qū)別是基于延遲的擁塞控制。請參閱擁塞控制部分。
與TCP一樣,uTP使用基于窗口的擁塞控制。每個socket具有一個max_window,其決定在任何給定的時間,socket可以具有處于 in-flight 狀態(tài)的最大字節(jié)數(shù)。已發(fā)送但尚未確認(rèn)的任何數(shù)據(jù)包都被視為處于 in-flight 狀態(tài),處于 in-flight 狀態(tài)的字節(jié)數(shù)被定義為 cur_window。
只有在 cur_window + packet_size <= min(max_window,wnd_size),socket 允許發(fā)送數(shù)據(jù)包。數(shù)據(jù)包大小可能會有所不同,請參閱數(shù)據(jù)包大小部分。wnd_size是對端的建議窗口。它設(shè)定了對端處于 in-flight 狀態(tài)的數(shù)據(jù)包數(shù)量上限。
如果 max_window 小于數(shù)據(jù)包大小,則實(shí)現(xiàn)可能違反上述規(guī)則,并且它會調(diào)度數(shù)據(jù)包以使平均 cur_window <= max_window。
每個socket記錄來自對端的最后一次延遲測量的狀態(tài)(reply_micro)。無論何時收到數(shù)據(jù)包,都會通過從主機(jī)當(dāng)前時間減去timestamp_microseconds字段值(參閱頭部格式部分)來更新此狀態(tài)(以微秒為單位)。每次發(fā)送數(shù)據(jù)包時,socket 的reply_micro值都會放入數(shù)據(jù)包標(biāo)頭的timestamp_difference_microseconds字段中。
與TCP不同,uTP中的序列號和ACK指的是數(shù)據(jù)包,而不是字節(jié)。這意味著uTP 在重新發(fā)送時無法重新打包數(shù)據(jù)。
每個socket會記錄發(fā)送數(shù)據(jù)包時要使用的下一個序列號seq_nr。它還記錄上次接收的序列號ack_nr。最舊的未確認(rèn)的數(shù)據(jù)包是seq_nr - cur_window。
頭部格式
version 1 頭部:
所有字段都按網(wǎng)絡(luò)字節(jié)順序(大端)。
type
type字段描述了數(shù)據(jù)包的類型。它可以是以下之一:
-
ST_DATA = 0
常規(guī)數(shù)據(jù)包。套接字處于連接狀態(tài)并具有要發(fā)送的數(shù)據(jù)。ST_DATA數(shù)據(jù)包始終具有數(shù)據(jù)有效負(fù)載。 -
ST_FIN = 1
終止連接。這是最后一個數(shù)據(jù)包。類似于TCP FIN標(biāo)志,關(guān)閉連接。屬于該連接的數(shù)據(jù)包,序列號永遠(yuǎn)不會有大于此數(shù)據(jù)包中的序列號。socket將此序列號記錄為eof_pkt。即使在收到ST_FIN數(shù)據(jù)包后,socket仍會等待一段時間,接收那些可能是丟失的或亂序到達(dá)的數(shù)據(jù)包。 -
ST_STATE = 2
狀態(tài)包。用于傳輸沒有數(shù)據(jù)的ACK。不包含任何有效負(fù)載的數(shù)據(jù)包不會增加seq_nr。 -
ST_RESET = 3
強(qiáng)行終止連接。與TCP RST標(biāo)志類似。遠(yuǎn)程主機(jī)沒有此連接的任何狀態(tài),標(biāo)識它是陳舊的,應(yīng)該被終止。 -
ST_SYN = 4
連接SYN。與TCP SYN標(biāo)志類似,此數(shù)據(jù)包啟動連接。序列號初始化為1。connection ID 初始化為隨機(jī)數(shù)。syn數(shù)據(jù)包是特殊的,在此連接上發(fā)送的所有后續(xù)數(shù)據(jù)包(除了重新發(fā)送的ST_SYN)都使用(connection ID + 1)發(fā)送。connection ID 是另一端在其響應(yīng)中使用的。當(dāng)接收到ST_SYN時,應(yīng)使用包頭中的ID初始化新socket。應(yīng)將socket的發(fā)送ID初始化為ID + 1。返回通道的序列號初始化為隨機(jī)數(shù)。另一端期望收到響應(yīng)ST_STATE包(僅ACK)。
version
協(xié)議版本。目前的版本是1。
connection_id
這是一個隨機(jī)的唯一編號,用于標(biāo)識屬于同一連接的所有數(shù)據(jù)包。每個socket都有一個用于發(fā)送數(shù)據(jù)包的連接ID和用于接收數(shù)據(jù)包的不同連接ID。啟動連接的端點(diǎn)決定使用哪個ID,返回路徑具有相同的ID + 1。
timestamp_microseconds
這是發(fā)送此數(shù)據(jù)包的時間戳的“微秒”部分。這是在posix上使用gettimeofday()和在windows上使用QueryPerformanceTimer()設(shè)置的。時間戳精度越高越好,時間戳設(shè)定越接近實(shí)際傳輸時間越好。
timestamp_difference_microseconds
這是收到最新的一個數(shù)據(jù)包時,數(shù)據(jù)包的時間戳和本地時間之間的差值。這是從遠(yuǎn)端到本地的鏈路最新單向延遲測量。
當(dāng)新打開socekt并且還沒有任何延遲樣本時,必須將其設(shè)置為0。
wnd_size
通知接收窗口。這是32位寬,以字節(jié)為單位指定。
window size是指當(dāng)前正在傳輸?shù)淖止?jié)數(shù),即已發(fā)送但未被確認(rèn)ACK。當(dāng)接收緩沖區(qū)即將被填滿時,如果它不能更快??地接收,則通知接收窗口可以讓另一端修改窗口大小。
發(fā)送數(shù)據(jù)包時,應(yīng)將其設(shè)置為socket接收緩沖區(qū)中剩余的字節(jié)數(shù)。
seq_nr
這是該數(shù)據(jù)包的序列號。與TCP相反,uTP序列號不是指字節(jié),而是指數(shù)據(jù)包。序列號告訴另一端應(yīng)該將數(shù)據(jù)包送回應(yīng)用層的順序。
ack_nr
這是數(shù)據(jù)包的發(fā)送者最后在對端接收的序列號。
extension
擴(kuò)展鏈表中第一個擴(kuò)展的類型。0表示沒有擴(kuò)展名。
目前有一個擴(kuò)展:
擴(kuò)展是鏈接形式的,就像TCP options一樣。如果擴(kuò)展字段非零,則緊跟在uTP頭之后是兩個字節(jié):
其中extension指定鏈表中下一個擴(kuò)展的類型,0終止列表。len指定這個擴(kuò)展的字節(jié)數(shù)。只需向前l(fā)en個字節(jié)就可以跳過未知的擴(kuò)展名。
SELECTIVE ACK
Selective ACK是一種非順序的、可選擇性的ACK包擴(kuò)展。其有效載荷是至少32位的位掩碼,長度是32位的倍數(shù)。每個位代表發(fā)送窗口中的一個數(shù)據(jù)包。發(fā)送窗口之外的位將被忽略。設(shè)置位(1)指定已接收數(shù)據(jù)包,清除位(0)指定尚未接收數(shù)據(jù)包。頭部格式如下:
請注意,擴(kuò)展名的len字段指的是字節(jié),在此擴(kuò)展名中必須至少為4,并且為4的倍數(shù)。
僅當(dāng)在接收的流中跳過至少一個序列號時才發(fā)送選擇性ACK。因此,掩碼中的第一個比特表示ack_nr + 2。當(dāng)發(fā)送該包時,則ack_nr + 1被假設(shè)為已被丟棄或丟失。設(shè)置位表示已接收的數(shù)據(jù)包,清除位表示尚未接收的數(shù)據(jù)包。
位掩碼具有反向字節(jié)順序。第一個字節(jié)以相反的順序表示包[ack_nr + 2,ack_nr + 2 + 7]。字節(jié)中的最低有效位表示ack_nr + 2,字節(jié)中的最高有效位表示ack_nr + 2 + 7。掩碼中的下一個字節(jié)以相反的順序表示[ack_nr + 2 + 8,ack_nr + 2 + 15],等等。位掩碼不限于32位,而是可以是任何大小。
以下是位掩碼的布局,表示在Selective ACK位域中表示的前32個包的acks:
圖中的數(shù)字將位掩碼中的位映射到偏移量,然后加上 ack_nr來計算該位要確認(rèn)的序列號。
建立連接
下圖說明了啟動連接的通信和狀態(tài)。c.*指的是socket本身的狀態(tài),pkt.*指的是包頭中的字段。
連接由conn_id標(biāo)頭標(biāo)識。如果新連接的連接ID與現(xiàn)有連接沖突,則連接嘗試將失敗,因為ST_SYN數(shù)據(jù)包在現(xiàn)有流中將是異常的,并被忽略。
包丟失
如果序列號(seq_nr - cur_window)的數(shù)據(jù)包尚未被確認(rèn)(這是發(fā)送緩沖區(qū)中最早的數(shù)據(jù)包,也是需要被確認(rèn)的下一個數(shù)據(jù)包),但跳過該數(shù)據(jù)包之后的3個或更多數(shù)據(jù)包已被確認(rèn)(通過Selective ACK),則假設(shè)該數(shù)據(jù)包丟失了。類似地,當(dāng)接收到3個重復(fù)的ack時,假設(shè)ack_nr + 1已經(jīng)丟失(如果已經(jīng)發(fā)送了具有該序列號的數(shù)據(jù)包)。
這也適用于Selective ACK。在選擇性確認(rèn)消息中被確認(rèn)的每個包被計為一個重復(fù)的ack,如果它>=3,則在其之后的具有>=3個被確認(rèn)的數(shù)據(jù)包都應(yīng)該被重新發(fā)送。
當(dāng)數(shù)據(jù)包丟失時,max_window乘以0.5以模擬TCP。
超時
通過落入范圍(last_ack_nr,ack_nr] 或明確地被Selective ACK消息被確認(rèn)的每個數(shù)據(jù)包都應(yīng)該用于更新rtt(往返時間)和rtt_var(rtt方差)測量。這里的 last_ack_nr 是 socket 在當(dāng)前數(shù)據(jù)包之前收到的最后一個ack_nr,ack_nr是當(dāng)前數(shù)據(jù)包中的字段。
該 rrt 和 rtt_var 只更新已發(fā)送且只發(fā)送了一次的數(shù)據(jù)包。這避免了需要知道是哪個數(shù)據(jù)包被確認(rèn)的問題,第一個或第二個數(shù)據(jù)包。
每次確認(rèn)數(shù)據(jù)包時,rtt 和 rtt_var 都由以下公式計算:
delta = rtt - packet_rtt rtt_var +=(abs(delta) - rtt_var) / 4; rtt += (packet_rtt - rtt) / 8;每次更新 rtt 和 rtt_var 時,也會更新與socket關(guān)聯(lián)的數(shù)據(jù)包的默認(rèn)超時 。它被設(shè)置為:
timeout = max(rtt + rtt_var * 4, 500);其中timeout以毫秒為單位指定。即數(shù)據(jù)包的最小超時為1/2秒。
每次socket發(fā)送或接收數(shù)據(jù)包時,它都會更新其timeout計數(shù)器。如果從上一次timeout計數(shù)器重置開始,在timeout毫秒內(nèi)沒有數(shù)據(jù)包到達(dá),則socket觸發(fā)超時。它將 packet_size 和 max_window 設(shè)置為最小的數(shù)據(jù)包大小(150字節(jié))。這允許它再發(fā)送一個數(shù)據(jù)包,這就是如果窗口大小降到零,套接字再次啟動的方式。
初始超時設(shè)置為1000毫秒,稍后根據(jù)上面的公式進(jìn)行更新。對于超時的數(shù)據(jù)包以及后續(xù)的每個數(shù)據(jù)包,超時加倍。
包大小
為了盡可能減少對慢速擁塞鏈路的影響,uTP將其數(shù)據(jù)包大小調(diào)整為每個數(shù)據(jù)包150個字節(jié)。使用較小的數(shù)據(jù)包具有不會阻塞慢速上行鏈路的優(yōu)點(diǎn),具有較長的序列化延遲。使用小數(shù)據(jù)包的成本使得數(shù)據(jù)包報頭的開銷變得很大。在高速率下,使用大的包大小,慢速率下使用小的包大小。
擁塞控制
uTP擁塞控制的總體目標(biāo)是使用單向緩沖延遲作為主要擁塞測量,丟包也是如此,和TCP類似。關(guān)鍵是要避免在發(fā)送數(shù)據(jù)時使用全部的發(fā)送緩沖區(qū)運(yùn)行。這對于DSL / Cable調(diào)制解調(diào)器來說尤其是一個問題,其中調(diào)制解調(diào)器中的發(fā)送緩沖器通常具有多秒數(shù)據(jù)的空間。uTP(或任何后臺流量協(xié)議)的理想緩沖區(qū)利用率是以0字節(jié)緩沖區(qū)利用率運(yùn)行。即任何其他流量可以在任何時間發(fā)送而不受阻礙。實(shí)際上,uTP目標(biāo)延遲設(shè)置為100 ms。每個socket的目的是永遠(yuǎn)不會在發(fā)送鏈路上看到超過100毫秒的延遲,如果有,它會減速。這有效地使uTP流量屈服于任何TCP流量。
這是通過在uTP發(fā)送的每個數(shù)據(jù)包中包含高精度時間戳來實(shí)現(xiàn)的,并且接收端計算其自己的高精度計時器與其接收的數(shù)據(jù)包中的時間戳之間的差異。然后將該差異反饋給包的原始發(fā)送方(timestamp_difference_microseconds)。該值作為絕對值沒有意義。機(jī)器中的時鐘很可能不同步,尤其是不低于微秒分辨率,并且數(shù)據(jù)包在傳輸?shù)臅r間也包含在這些時間戳的差異中。但是,與以前的timestamp_difference_microseconds值相比,該值很有用。
每個socket記錄在最后兩分鐘內(nèi)的滑動最小值。此值稱為base_delay,用作基線,即主機(jī)之間的最小延遲。當(dāng)從每個數(shù)據(jù)包中的時間戳差異中減去base_delay時,您將獲得socket上當(dāng)前緩沖延遲的度量。此測量稱為our_delay。它有很多噪音,但用作驅(qū)動程序來確定是增加還是減少發(fā)送窗口(控制發(fā)送速率)。
CCONTROL_TARGET是UTP接受上行鏈路上的緩沖延遲。目前,延遲目標(biāo)設(shè)置為100毫秒。off_target是實(shí)際測量延遲距目標(biāo)延遲的距離(從CCONTROL_TARGET - our_delay計算得出)。
socket結(jié)構(gòu)中的窗口大小指定了連接上我們可能在in_flight中(未確認(rèn))的字節(jié)數(shù)。發(fā)送速率與此窗口大小直接相關(guān)。飛行中的字節(jié)越多,發(fā)送速率越快。在代碼中,窗口大小稱為 max_window。它的大小大致由以下表達(dá)式控制:
delay_factor = off_target / CCONTROL_TARGET; window_factor = outstanding_packet / max_window; scaled_gain = MAX_CWND_INCREASE_PACKETS_PER_RTT * delay_factor * window_factor;第一個因子將off_target縮放到目標(biāo)延遲的單位。
然后將scaled_gain添加到max_window:
max_window += scaled_gain;如果off_target大于0,這將使窗口變小,如果off target小于0,則增大窗口。
如果max_window小于0,則將其設(shè)置為0。窗口大小為零意味著socket可能不發(fā)送任何數(shù)據(jù)包。在此狀態(tài)下,socekt將觸發(fā)超時并將窗口大小強(qiáng)制為一個數(shù)據(jù)包大小,并發(fā)送一個數(shù)據(jù)包。有關(guān)更多信息,請參閱有關(guān)超時的部分。
總結(jié)
以上是生活随笔為你收集整理的uTorrent transport protocol的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: vim c语言编辑器,Vim编辑器简单使
- 下一篇: 【解决方案】win7计算机屏保的下拉框中