<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          為什么 TCP 會被 UDP 取代?

          共 7196字,需瀏覽 15分鐘

           ·

          2020-08-21 12:07

          ??Python貓” ,一個值得加星標(biāo)的公眾號

          花下貓語:今天分享的文章來自“為什么這么設(shè)計”系列,它跟我正在創(chuàng)造的“Python為什么”系列一樣,都有很強(qiáng)的問題意識,都關(guān)注技術(shù)原理與設(shè)計上的權(quán)衡抉擇。該系列的話題基本是比較廣泛的程序/軟件設(shè)計類問題,篇篇都是干貨長文,推薦大家一讀~

          劇照 | 《且聽鳳鳴》

          為什么這么設(shè)計(Why's THE Design)是一系列關(guān)于計算機(jī)領(lǐng)域中程序設(shè)計決策的文章,我們在這個系列的每一篇文章中都會提出一個具體的問題并從不同的角度討論這種設(shè)計的優(yōu)缺點、對具體實現(xiàn)造成的影響。如果你有想要了解的問題,可以在文章下面留言。

          來源:Draveness@真沒什么邏輯

          TCP 協(xié)議可以說是今天互聯(lián)網(wǎng)的基石,作為可靠的傳輸協(xié)議,在今天幾乎所有的數(shù)據(jù)都會通過 TCP 協(xié)議傳輸,然而 TCP 在設(shè)計之初沒有考慮到現(xiàn)今復(fù)雜的網(wǎng)絡(luò)環(huán)境,當(dāng)你在地鐵上或者火車上被斷斷續(xù)續(xù)的網(wǎng)絡(luò)折磨時,你可能都不知道這一切可能都是 TCP 協(xié)議造成的。本文會分析 TCP 協(xié)議為什么在弱網(wǎng)環(huán)境下有嚴(yán)重的性能問題[^1]。

          底層的數(shù)據(jù)傳輸協(xié)議在設(shè)計時必須要對帶寬的利用率和通信延遲進(jìn)行權(quán)衡和取舍,所以想要解決實際生產(chǎn)中的全部問題是不可能的,TCP 選擇了充分利用帶寬,為流量而設(shè)計,期望在盡可能短的時間內(nèi)傳輸更多的數(shù)據(jù)[^2]。

          在網(wǎng)絡(luò)通信中,從發(fā)送方發(fā)出數(shù)據(jù)開始到收到來自接收方的確認(rèn)的時間被叫做往返時延(Round-Trip Time,RTT)。

          弱網(wǎng)環(huán)境是丟包率較高的特殊場景,TCP 在類似場景中的表現(xiàn)很差,當(dāng) RTT 為 30ms 時,一旦丟包率達(dá)到了 2%,TCP 的吞吐量就會下降 89.9%[^3],從下面的表中我們可以看出丟包對 TCP 的吞吐量極其顯著的影響:

          RTTTCP 吞吐量TCP 吞吐量(2% 丟包率)
          0 ms93.5 Mbps3.72 Mbps
          30 ms16.2 Mbps1.63 Mbps
          60 ms8.7 Mbps1.33 Mbps
          90 ms5.32 Mbps0.85 Mbps

          本文將分析在弱網(wǎng)環(huán)境下(丟包率高)影響 TCP 性能的三個原因:

          • TCP 的擁塞控制算法會在丟包時主動降低吞吐量;
          • TCP 的三次握手增加了數(shù)據(jù)傳輸?shù)难舆t和額外開銷;
          • TCP 的累計應(yīng)答機(jī)制導(dǎo)致了數(shù)據(jù)段的傳輸;

          在上述的三個原因中,擁塞控制算法是導(dǎo)致 TCP 在弱網(wǎng)環(huán)境下有著較差表現(xiàn)的首要原因,三次握手和累計應(yīng)答兩者的影響依次遞減,但是也加劇了 TCP 的性能問題。

          擁塞控制

          TCP 擁塞控制算法是互聯(lián)網(wǎng)上主要的擁塞控制措施,它使用一套基于線増積減(Additive increase/multiplicative decrease,AIMD)的網(wǎng)絡(luò)擁塞控制方法來控制擁塞[^4],也是造成 TCP 性能問題的主要原因。

          第一次發(fā)現(xiàn)的互聯(lián)網(wǎng)擁塞崩潰是在 1986 年,NSFnet 階段一的骨干網(wǎng)的處理能力從 32,000bit/s 降到了 40bit/s,該骨干網(wǎng)的處理能力直到 1987 和 1988 年,TCP 協(xié)議實現(xiàn)了擁塞控制之后才得到解決[^5]。正是因為發(fā)生過網(wǎng)絡(luò)阻塞造成的崩潰,所以 TCP 的擁塞控制算法就認(rèn)為只要發(fā)生了丟包當(dāng)前網(wǎng)絡(luò)就發(fā)生了擁堵,從這一假設(shè)出發(fā),TCP 就使用了慢啟動和線增積減[^6]的機(jī)制實現(xiàn)擁塞控制。

          tcp-congestion-control

          圖 1 - TCP 的擁塞控制機(jī)制

          每一個 TCP 連接都會維護(hù)一個擁塞控制窗口(Congestion Window),擁塞控制窗口的作用有兩個:

          1. 防止發(fā)送方向接收方發(fā)送了太多數(shù)據(jù),導(dǎo)致接收方無法處理;
          2. 防止 TCP 連接的任意一方向網(wǎng)絡(luò)中發(fā)送大量數(shù)據(jù),導(dǎo)致網(wǎng)絡(luò)擁塞崩潰;

          除了擁塞窗口大小(cwnd)之外,TCP 連接的雙方都有接收窗口大小(rwnd),在 TCP 連接建立之初,發(fā)送方和接收方都不清楚對方的接收窗口大小,所以通信雙方需要一套動態(tài)的估算機(jī)制改變數(shù)據(jù)傳輸?shù)乃俣龋?TCP 三次握手期間,通信雙方會通過 ACK 消息通知對方自己的接收窗口大小,接收窗口大小一般是帶寬延遲乘積(Bandwidth-delay product, BDP)決定的[^7],不過在這里我們就不展開介紹了。

          客戶端能夠同時傳輸?shù)淖畲髷?shù)據(jù)段的數(shù)量是接收窗口大小和擁塞窗口大小的最小值,即?min(rwnd, cwnd)。TCP 連接的初始擁塞窗口大小是一個比較小的值,在 Linux 中是由?TCP_INIT_CWND?定義的[^8]:

          /* TCP initial congestion window as per rfc6928 */#define TCP_INIT_CWND		10

          初始擁塞控制窗口的大小從出現(xiàn)之后被多次修改,幾個名為 Increasing TCP's Initial Window 的 RFC 文檔:RFC2414[^9]、RFC3390[^10] 和 RFC6928[^11] 分別增加了?initcwnd?的值以適應(yīng)不斷提高的網(wǎng)絡(luò)傳輸速度和帶寬。

          tcp-congestion-window

          圖 2 - TCP 擁塞控制算法的線増積減

          如上圖所示,TCP 連接發(fā)送方的擁塞控制窗口大小會根據(jù)接收方的響應(yīng)而變化:

          1. 線性增長:經(jīng)過 1 個 RTT ,擁塞窗口大小會加一;
          2. 積式減少:當(dāng)發(fā)送方發(fā)送的數(shù)據(jù)包丟包時,擁塞控制閾值會減半;

          如果 TCP 連接剛剛建立,由于 Linux 系統(tǒng)的默認(rèn)設(shè)置,客戶端能夠同時發(fā)送 10 個數(shù)據(jù)段,假設(shè)我們網(wǎng)絡(luò)的帶寬是 10M,RTT 是 40ms,每個數(shù)據(jù)段的大小是 1460 字節(jié),那么使用 BDP 計算的通信雙方窗口大小上限應(yīng)該是 35,這樣才能充分利用網(wǎng)絡(luò)的帶寬:


          然而擁塞控制窗口的大小從 10 漲到 35 需要 2RTT 的時間,具體的過程如下:

          1. 發(fā)送方向接收方發(fā)送?initcwnd = 10?個數(shù)據(jù)段(消耗 0.5RTT);
          2. 接收方接收到 10 個數(shù)據(jù)段后向發(fā)送方發(fā)送 ACK(消耗 0.5RTT);
          3. 發(fā)送方接收到發(fā)送方的 ACK,擁塞控制窗口大小由于 10 個數(shù)據(jù)段的成功發(fā)送 +10,當(dāng)前擁塞控制窗口大小達(dá)到 20;
          4. 發(fā)送方向接收方發(fā)送 20 個數(shù)據(jù)段(消耗 0.5RTT);
          5. 接收方接收到 20 個數(shù)據(jù)段后向發(fā)送方發(fā)送 ACK(消耗 0.5RTT);
          6. 發(fā)送方接收到發(fā)送方的 ACK,擁塞控制窗口大小由于 20 個數(shù)據(jù)段的成功發(fā)送 +20,當(dāng)前擁塞控制窗口大小達(dá)到 40;

          從 TCP 三次握手建立連接到擁塞控制窗口大小達(dá)到假定網(wǎng)絡(luò)狀況的最大值 35 需要 3.5RTT 的時間,即 140ms,這是一個比較長的時間了。

          早期互聯(lián)網(wǎng)的大多數(shù)計算設(shè)備都通過有線網(wǎng)絡(luò)連接,出現(xiàn)網(wǎng)絡(luò)不穩(wěn)定的可能性也比較低,所以 TCP 協(xié)議的設(shè)計者認(rèn)為丟包意味著網(wǎng)絡(luò)出現(xiàn)擁塞,一旦發(fā)生丟包,客戶端瘋狂重試就可能導(dǎo)致互聯(lián)網(wǎng)的擁塞崩潰,所以發(fā)明了擁塞控制算法來解決該問題。

          但是如今的網(wǎng)絡(luò)環(huán)境更加復(fù)雜,無線網(wǎng)絡(luò)的引入導(dǎo)致部分場景下的網(wǎng)絡(luò)不穩(wěn)定成了常態(tài),所以丟包并不一定意味著網(wǎng)絡(luò)擁堵,如果使用更加激進(jìn)的策略傳輸數(shù)據(jù),在一些場景下會得到更好的效果。

          三次握手

          TCP 使用三次握手建立連接應(yīng)該是全世界所有工程師都十分了解的知識點,三次握手的主要目的是避免歷史錯誤連接的建立并讓通信的雙方確定初始序列號[^12],然而三次握手的成本相當(dāng)高,在不丟包的情況下,它需要建立 TCP 連接的雙方進(jìn)行三次通信。

          basic-3-way-handshake

          圖 3 - 常見的 TCP 三次握手

          如果我們要從北京訪問上海的服務(wù)器,由于北京到上海的直線距離約為 1000 多公里,而光速是目前通信速度的極限,所以 RTT 一定會大于 6.7ms:


          然而因為光在光纖中不是直線傳播的,真正的傳輸速度會比光速慢 ~31%[^13],而且數(shù)據(jù)需要在各種網(wǎng)絡(luò)設(shè)備之間來回跳轉(zhuǎn),所以很難達(dá)到理論的極限值。在生產(chǎn)環(huán)境中從北京到上海的 RTT 大概在 40ms 左右,所以 TCP 建立連接所需要最短時間也需要 60ms(1.5RTT)。

          在網(wǎng)絡(luò)環(huán)境較差的地鐵、車站等場景中,因為丟包率較高,客戶端很難與服務(wù)端快速完成三次通信并建立 TCP 連接。當(dāng)客戶端長時間沒有收到服務(wù)端的響應(yīng)時,只能不斷發(fā)起重試,隨著請求次數(shù)逐漸增加,訪問的延遲也會越來越高。

          由于大多數(shù)的 HTTP 請求都不會攜帶大量的數(shù)據(jù),未被壓縮的請求和響應(yīng)頭大小在 ~200B 到 2KB 左右,而 TCP 三次握手帶來的額外開銷是 222 字節(jié),其中以太網(wǎng)數(shù)據(jù)幀占?3 * 14 = 42?字節(jié),IP 數(shù)據(jù)幀占?3 * 20 = 60?字節(jié),TCP 數(shù)據(jù)幀占 120 字節(jié):

          tcp-three-way-handshake-overhead

          圖 4 - TCP 三次握手的額外開銷

          雖然 TCP 不會為每一個發(fā)出的數(shù)據(jù)段建立連接,但是三次握手建立連接需要的成本還是相當(dāng)高,不僅需要額外增加 1.5RTT 的網(wǎng)絡(luò)延時,還需要增加 222 字節(jié)的額外開銷,所以在弱網(wǎng)環(huán)境下,通過三次握手建立連接會加劇 TCP 的性能問題。

          重傳機(jī)制

          TCP 傳輸?shù)目煽啃允峭ㄟ^序列號和接收方的 ACK 來保證的,當(dāng) TCP 傳輸一個數(shù)據(jù)段時,它會將該數(shù)據(jù)段的副本放到重傳隊列上并開啟計時器[^14]:

          • 如果發(fā)送方收到了該數(shù)據(jù)段對應(yīng)的 ACK 響應(yīng),當(dāng)前數(shù)據(jù)段就會從重傳隊列中刪除;
          • 如果發(fā)送方在計時器到期之間都沒有收到該數(shù)據(jù)段對應(yīng)的 ACK,就會重新發(fā)送當(dāng)前數(shù)據(jù)段;

          TCP 的 ACK 機(jī)制可能會導(dǎo)致發(fā)送方重新傳輸接收方已經(jīng)收到了數(shù)據(jù)段。TCP 中的 ACK 消息表示該消息之前的全部消息都已經(jīng)被成功接收和處理,例如:

          1. 發(fā)送方向接收方發(fā)送了序號為 1-10 的消息;
          2. 接收方向發(fā)送方發(fā)送 ACK 8 響應(yīng);
          3. 發(fā)送方認(rèn)為序號為 1-8 的消息已經(jīng)被成功接收;

          這種 ACK 的方式在實現(xiàn)上比較簡單,更容易保證消息的順序性,但是在以下情況可能會導(dǎo)致發(fā)送方重傳已經(jīng)接收的數(shù)據(jù):

          tcp-retransmission-al

          圖 5 - TCP 的重傳策略

          如上圖所示,接收方已經(jīng)收到了序號為 2-5 的數(shù)據(jù),但是由于 TCP ACK 的語義是當(dāng)前數(shù)據(jù)段前的全部數(shù)據(jù)段都已經(jīng)被接收和處理,所以接收方無法發(fā)送 ACK 消息,由于發(fā)送方?jīng)]有收到 ACK,所有數(shù)據(jù)段對應(yīng)的計時器就會超時并重新傳輸數(shù)據(jù)。在丟包較為嚴(yán)重的網(wǎng)絡(luò)下,這種重傳機(jī)制會造成大量的帶寬浪費。

          總結(jié)

          TCP 協(xié)議的一些設(shè)計在今天來看雖然仍然具有巨大的價值,但是并不能適用于所有場景。為了解決 TCP 的性能問題,目前業(yè)界有兩種解決方案:

          1. 使用 UDP 構(gòu)建性能更加優(yōu)異、更靈活的傳輸協(xié)議,例如:QUIC[^15] 等;
          2. 通過不同的手段優(yōu)化 TCP 協(xié)議的性能,例如:選擇性 ACK(Selective ACK, SACK)[^16],TCP 快開啟(TCP Fast Open, TFO)[^17];

          由于 TCP 協(xié)議在操作系統(tǒng)內(nèi)核中,不利于協(xié)議的更新,所以第一種方案目前發(fā)展的更好,HTTP/3 就使用了 QUIC 作為傳輸協(xié)議[^18]。我們在這里重新回顧一下導(dǎo)致 TCP 性能問題的三個重要原因:

          • TCP 的擁塞控制在發(fā)生丟包時會進(jìn)行退讓,減少能夠發(fā)送的數(shù)據(jù)段數(shù)量,但是丟包并不一定意味著網(wǎng)絡(luò)擁塞,更多的可能是網(wǎng)絡(luò)狀況較差;
          • TCP 的三次握手帶來了額外開銷,這些開銷不只包括需要傳輸更多的數(shù)據(jù),還增加了首次傳輸數(shù)據(jù)的網(wǎng)絡(luò)延遲;
          • TCP 的重傳機(jī)制在數(shù)據(jù)包丟失時可能會重新傳輸已經(jīng)成功接收的數(shù)據(jù)段,造成帶寬的浪費;

          TCP 協(xié)議作為互聯(lián)網(wǎng)數(shù)據(jù)傳輸?shù)幕梢哉f是當(dāng)之無愧,雖然它確實在應(yīng)對特殊場景時有些問題,但是它的設(shè)計思想有著非常多的借鑒意義并值得我們學(xué)習(xí)。

          到最后,我們還是來看一些比較開放的相關(guān)問題,有興趣的讀者可以仔細(xì)思考一下下面的問題:

          • QUIC 協(xié)議是能否保證丟包率較高時的傳輸性能?
          • 除了 SACK 和 TFO 之外還有哪些手段可以優(yōu)化 TCP 的性能?

          如果對文章中的內(nèi)容有疑問或者想要了解更多軟件工程上一些設(shè)計決策背后的原因,可以在博客下面留言,作者會及時回復(fù)本文相關(guān)的疑問并選擇其中合適的主題作為后續(xù)的內(nèi)容。

          [^1]: TCP Selective Acknowledgment Options, October 1996 https://tools.ietf.org/html/rfc2018

          [^2]: KCP - A Fast and Reliable ARQ Protocol https://github.com/skywind3000/kcp

          [^3]: Measuring Network Performance: Links Between Latency, Throughput and Packet Loss https://accedian.com/enterprises/blog/measuring-network-performance-latency-throughput-packet-loss/

          [^4]: Wikipedia: TCP congestion control https://en.wikipedia.org/wiki/TCP_congestion_control

          [^5]: Wikipedia: Network congestion https://en.wikipedia.org/wiki/Network_congestion#Congestive_collapse

          [^6]: Wikipedia: Additive increase/multiplicative decrease https://en.wikipedia.org/wiki/Additive_increase/multiplicative_decrease

          [^7]: Bandwidth-delay product https://en.wikipedia.org/wiki/Bandwidth-delay_product

          [^8]: TCP_INIT_CWND https://github.com/torvalds/linux/blob/738d2902773e30939a982c8df7a7f94293659810/include/net/tcp.h#L226

          [^9]: RFC2414 Increasing TCP's Initial Window https://tools.ietf.org/html/rfc2414

          [^10]: RFC3390 Increasing TCP's Initial Window https://tools.ietf.org/html/rfc3390

          [^11]: RFC6928 Increasing TCP's Initial Window https://tools.ietf.org/html/rfc6928

          [^12]: 為什么 TCP 建立連接需要三次握手, October 2019 https://draveness.me/whys-the-design-tcp-three-way-handshake

          [^13]: Researchers create fiber network that operates at 99.7% speed of light, smashes speed and latency records, March 2013 https://www.extremetech.com/computing/151498-researchers-create-fiber-network-that-operates-at-99-7-speed-of-light-smashes-speed-and-latency-records

          [^14]: RFC793 Transmission Control Protocol, September 1981 RFC793 https://tools.ietf.org/html/rfc793

          [^15]: Wikiepedia: QUIC https://en.wikipedia.org/wiki/QUIC

          [^16]: RFC018 TCP Selective Acknowledgment Options, October 1996 https://tools.ietf.org/html/rfc2018

          [^17]: RFC7413 TCP Fast Open, December 2014 https://tools.ietf.org/html/rfc7413

          [^18]: HTTP-over-QUIC to be renamed HTTP/3, November 2018 https://www.zdnet.com/article/http-over-quic-to-be-renamed-http3/

          相關(guān)文章推薦

          優(yōu)質(zhì)文章,推薦閱讀:

          Python 函數(shù)為什么會默認(rèn)返回 None?

          還傻傻分不清 Cookie、Session、Token、JWT?

          Python 的縮進(jìn)是不是反人類的設(shè)計?

          11 個最佳的 Python 編譯器和解釋器

          感謝創(chuàng)作者的好文
          瀏覽 58
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲免费看片 | 一级A片电影A片录像 | 淫秽三级片中文字幕在线免费观看 | 请立即播放黑人大黑吊日白人小嫩逼视频 | 亚洲天堂电影 |