理解 TCP(五):可靠性交付的实现
更好閱讀體驗:《理解 TCP 和 UDP》— By Gitbook
TCP 是一種提供可靠性交付的協(xié)議。
也就是說,通過 TCP 連接傳輸?shù)臄?shù)據(jù),無差錯、不丟失、不重復、并且按序到達。
但是在網(wǎng)絡(luò)中相連兩端之間的介質(zhì),是復雜的,并不確保數(shù)據(jù)的可靠性交付,那么 TCP 是怎么樣解決問題的?
這就需要了解 TCP 的幾種技術(shù):
滑動窗口
超時重傳
流量控制
擁塞控制
下面來分別講一下這幾種技術(shù)的實現(xiàn)原理。
超時重傳
重傳時機
TCP 報文段在傳輸?shù)倪^程中,下面的情況都是有可能發(fā)生的:
數(shù)據(jù)包中途丟失;
數(shù)據(jù)包順利到達,但對方發(fā)送的 ACK 報文中途丟失;
數(shù)據(jù)包順利到達,但對方異常未響應 ACK 或被對方丟棄;
當出現(xiàn)這些異常情況時,TCP 就會超時重傳。
TCP 每發(fā)送一個報文段,就對這個報文段設(shè)置一次計時器。只要計時器設(shè)置的重傳時間到了,但還沒有收到確認,就重傳這一報文段,這個就叫做「超時重傳」。
重傳算法
先認識兩個概念
RTO ( Retransmission Time-Out ) 重傳超時時間
指發(fā)送端發(fā)送數(shù)據(jù)后、重傳數(shù)據(jù)前等待接收方收到該數(shù)據(jù) ACK 報文的時間。
大白話就是,需要等待多長時間還沒收到確認,就重新傳一次。
RTO 的設(shè)置對于重傳非常重要:
設(shè)長了,重發(fā)就慢,沒有效率,性能差;
設(shè)短了,重發(fā)得就快,會增加網(wǎng)絡(luò)擁塞,導致更多的超時,更多的超時導致更多的重發(fā)。
RTT ( Round Trip Time ) 連接往返時間
指發(fā)送端從發(fā)送 TCP 包開始到接收它的 ACK 報文之間所耗費的時間。
而在實際的網(wǎng)絡(luò)傳輸中,RTT 的值每次都是隨機的,無法事先預預知。
TCP 通過測量來獲得連接當前 RTT 的一個估計值,并以該 RTT 估計值為基準來設(shè)置當前的 RTO。
這就引入了一類算法的稱呼:自適應重傳算法(Adaptive Restransmission Algorithm)
這類算法的關(guān)鍵就在于對當前 RTT 的準確估計,以便適時調(diào)整 RTO。
關(guān)于自適應重傳算法,經(jīng)歷過多次的迭代和修正。
從 1981 年的 RFC793 提及的經(jīng)典算法,到 1987 年 Karn 提出的 Karn/Partridge 算法,再到后來的 1988 年的 Jacobson / Karels 算法。
最后的這個算法在被用在今天的 TCP 協(xié)議中(Linux的源代碼在:tcp_rtt_estimator)。
自適應重傳算法的發(fā)展讀者有興趣可以參考其他資料,在這里我拎一個現(xiàn)在在用的算法出來講講,隨意感受一下。
Jacobson / Karels 算法
1988年,有人推出來了一個新的算法,這個算法叫 Jacobson / Karels Algorithm(參看RFC6298)。
其計算公式:
SRTT = SRTT + α ( RTT – SRTT ) —— 計算平滑 RTT
DevRTT = ( 1-β ) DevRTT + β ( | RTT - SRTT | ) ——計算平滑 RTT 和真實的差距(加權(quán)移動平均)
RTO= μ SRTT + ? DevRTT
其中:
α、β、μ、? 是可以調(diào)整的參數(shù),在 RFC6298 中給出了對應的參考值,而在Linux下,α = 0.125,β = 0.25, μ = 1,? = 4;
SRTT 是 Smoothed RTT 的意思,是 RTT 的平滑計算值,即根據(jù)每次測量的 RTT 和舊的 RTT 進行運算,得出新的 RTT。SRTT 的值,會在每一次測量到 RTT 之后進行更新;
DevRTT 是 Deviation RTT 的意思,根據(jù)每次測量的 RTT 和舊的 SRTT 值進行運算,得出新的 DevRTT;
由算法可以知道 RTO 的值會根據(jù)每次測量的 RTT 值變化而變化,基本要點是 TCP 監(jiān)視每個連接的性能,由每一個 TCP 的連接情況推算出合適的 RTO 值,根據(jù)不同的網(wǎng)絡(luò)情況,自動修改 RTO 值,以適應負責的網(wǎng)絡(luò)變化。
擁塞控制
滑動窗口 Sliding Window
滑動窗口協(xié)議比較復雜,也是 TCP 協(xié)議的精髓所在。
TCP 頭里有一個字段叫 Window,叫 Advertised-Window,這個字段是接收端告訴發(fā)送端自己還有多少緩沖區(qū)可以接收數(shù)據(jù)。于是發(fā)送端就可以根據(jù)這個接收端的處理能力來發(fā)送數(shù)據(jù),而不會導致接收端處理不過來。
滑動窗口分為「接收窗口」和「發(fā)送窗口」
因為 TCP 協(xié)議是全雙工的,會話的雙方都可以同時接收和發(fā)送,那么就需要各自維護一個「發(fā)送窗口」和「接收窗口」。
發(fā)送窗口
大小取決于對端通告的接受窗口。
只有收到對端對于本端發(fā)送窗口內(nèi)字節(jié)的 ACK 確認,才會移動發(fā)送窗口的左邊界。
下圖是發(fā)送窗口的示意圖:
對于發(fā)送窗口,在緩存內(nèi)的數(shù)據(jù)有四種狀態(tài):
#1 已發(fā)送,并得到接收方 ACK 確認;
#2 已發(fā)送,但還未收到接收方 ACK;
#3 未發(fā)送,但接收方允許發(fā)送,接收方還有空間
#4 未發(fā)送,且接收方不允許發(fā)送,接收方?jīng)]有空間
如果下一刻,收到了接收方對于 32-36 字節(jié)序的數(shù)據(jù)包的 ACK 確認,那么發(fā)送方的窗口就會發(fā)生「滑動」。
并且發(fā)送下一個 46-51 字節(jié)序的數(shù)據(jù)包。
滑動窗口的概念,描述了 TCP 的數(shù)據(jù)是怎么發(fā)送,以及怎么接收的。
TCP 的滑動窗口是動態(tài)的,我們可以想象成小學常見的一個數(shù)學題,一個水池,體積 V,每小時進水量 V1, 出水量 V2。
當水池滿了就不允許再注入了,如果有個液壓系統(tǒng)控制水池大小,那么就可以控制水的注入速率和量了。
應用程序可以根據(jù)自身的處理能力變化,通過 API 來控制本端 TCP 接收窗口的大小,來進行流量控制。
接收窗口
大小取決于應用、系統(tǒng)、硬件的限制。
下圖是接收窗口的示意圖(找不到圖,唯有自己畫了):
相對于發(fā)送窗口,接受窗口在緩存內(nèi)的數(shù)據(jù)只有三種狀態(tài):
已接收已確認;
未接收,準備接收;
未接收,并未準備接收;
下一刻接收到來自發(fā)送端的 32-36 數(shù)據(jù)包,然后回送 ACK 確認報,并且移動接收窗口。
另外接收端相對于發(fā)送端還有不同的一點,只有前面所有的段都確認的情況下才會移動左邊界,
在前面還有字節(jié)未接收但收到后面字節(jié)的情況下,窗口不會移動,并不對后續(xù)字節(jié)確認,以此確保對端會對這些數(shù)據(jù)重傳。
假如 32-36 字節(jié)不是一個報文段的,而是每個字節(jié)一個報文段的話,那么就會分成了 5 個報文段。
在實際的網(wǎng)絡(luò)環(huán)境中,不能確保是按序收到的,其中會有一些早達到,一些遲到達。
如圖中的 34、35 字節(jié)序,先收到了,接收窗口也不會移動。
因為有可能 32、33 字節(jié)序會出現(xiàn)丟包或者超時,這時就需要發(fā)送端重發(fā)報文段了。
參考
The TCP/IP Guide
TCP 的那些事兒(下)
《后臺開發(fā) 核心技術(shù)與應用實踐》
《計算機網(wǎng)絡(luò)》
總結(jié)
以上是生活随笔為你收集整理的理解 TCP(五):可靠性交付的实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 06、主题 样式 国际化
- 下一篇: AC日记——最大子段和 洛谷 P1115