多點頭髮,少點代碼
本文已經收錄至我的GitHub,歡迎大家踊躍star 和 issues。
https://github.com/midou-tech/articles
本來想先更新TCP的基礎和TCP可靠性等問題的,但是被你們暗示了,就先更流量控制和擁塞控制了。希望龍叔講的你能搞清楚,如果有不清楚的,可以加龍叔微信一起探討。
流量控制
講流量控制之前先花簡短的話語絮叨下TCP基礎知識,詳細知識細節後面會出文章一一道來。
TCP是一種面向連接、保證可靠性、流式傳輸服務。面向連接就是建立鏈接,也就是面試常問的三次揮手建立鏈接,四次揮手斷開鏈接。保證可靠性到是很好理解,就是你發送的數據盡最大可能保證讓接收端接收到。流式傳輸 就是傳輸的數據是以字節流的形式發送和接受(不要硬是和我說,什麼字節流傳輸?明明物理層上都是波信號,這,抱拳。)
TCP傳輸數據都是建立鏈接之後才進行傳輸,傳輸的時候爲保證可靠性,也是採用確認應答機制。所謂確認應答機制就是發送數據之後必須收到確認消息,纔算一次有效傳輸。
舉個簡單栗子,就是你和別人交流之前必須叫別人一聲(這位先生你好…. ,這位帥哥你好….,這位同學你好…. 等等),對方收到你的消息,也會回覆一聲(你好,請問有什麼事?),緊接着纔會有下面的一堆交流。
試想如果沒有這個建立交流的過程,或者對方沒有回覆你,你的信息是無法可靠的發送出去的。不過在傳輸協議裏還有一種可以不考慮對方是否接受,只管自己消息發送出去即可,那就是UDP傳輸協議。
瞭解了TCP的確認應答機制之後方便講解下面的流量控制,接下來就好好看龍叔給你說流量控制到底是怎麼一回事。
流量控制是保證可靠傳輸的方法之一,所謂流量控制本質就是控制TCP發送數據的速率,不要讓發送太快或者太慢讓接收方來不解接收或者沒東西可收。具體的控制方法或者機制是利用滑動窗口機制實現對TCP傳輸速率的控制。
這樣一說,是不是感覺自己瞬間明白了一些,沒有那麼迷糊了。別急,下面還有硬核,一定讓你明白。
TCP報頭裏面有兩個字節專門表示滑動窗口大小,如下是TCP報頭。不用死記硬背,看看就行了。
看完這個圖應該很明確一點,TCP數據段的頭部中有2個字節專門表示窗口大小,這也就很明確另外一個問題,每個傳輸的TCP數據段都有2個字節表示窗口大小,而這2個字節就是流量控制的關鍵。
不要混淆TCP收發窗口和物理窗口,物理窗口大小是主機所配置的網卡緩衝區大小,一般是固定的,需要通過修改參數配置才能改變。
比如龍叔的網卡緩衝區的發送和接收緩衝大小。
[longshu /home/longshu] 15:04
$ cat /proc/sys/net/core/rmem_max
212992
[longshu /home/longshu] 15:04
$ cat /proc/sys/net/core/wmem_max
212992
TCP接收窗口大小是可變的(根據網絡傳輸情況自適應調整大小)。
來一個滑動窗口的示例。
上面已經給出龍叔的主機物理髮送和接收窗口大小都是212992字節,爲了講解方便,先假設如下條件。
- 發送端物理窗口爲1W字節
- 每個TCP數據段大小是1000字節
- 用每個數據段的首字段序號表示該數據段的序號
目前發送端收到了接收端發來的一個確認數據段,確認號爲3001,窗口大小爲5000,因此表示可以連續發送10個數據段,數據段序號從3001~7001。
此時已經達到對方能接受的最大值,如果繼續發送數據可能會造成網絡擁堵等問題,在沒有接收到對方的確認序號和窗口大小之前,我方是不能發送數據的。
等待一會收到對方發來的確認序號爲6001,窗口大小爲4000,這表示對方已經收到了3001~6001四個數據段,並且可以接收數據段大小爲4個,因此我方可以繼續發送4個數據段,由於之前發送出去的數據還沒完全收到確認,此時只能發送8001、9001、10001這三個數據段,發送之後繼續等待收到確認應答號和窗口大小,才能進行下一次發送。
此時收到一個10001序號,窗口大小爲6000,就可以連續發送10001~15001這6個數據段。如此往復的過程,就是基本的滑動窗口控制流量的過程。
滑動窗口工作過程如下圖。
過程圖解看起來會清晰很多,可以清楚的知道滑動窗口每一次如何變化,以及如何收發。我最開始學習時很難理解滑動這個概念,覺得很抽象。現在看來滑動窗口就是未接受確認應答的數據段。
通過滑動窗口控制流量的方法也被稱爲 以收訂發的原則。
突然想到一個有趣的問題,前段時間我面試過一個粉絲,問到流量控制,對於整個滑動窗口流程答得很流暢,看他答得這麼順暢肯定是準備充分,學習蠻不錯的。一想這流量控制就這點內容,就不多問了,準備問個簡單的問題就換下一個知識點。
於是我問他 假如對方給我確認應答序號,可接受窗口大小爲0,怎麼辦?
相信聰明的你一定知道如何回答了,不過龍叔還是絮叨下。
首先對方在確認應答時可以發送窗口大小爲0,這點是沒問題的。我方在收到對方窗口大小爲0時,不是一直等待着,此時我方會啓動一個定時器,定時發送一個試探報文給對方,試探報文沒有數據的,當對方回覆我方窗口大小不爲0時繼續傳輸數據;如果爲0,重新啓動計時器。
這個定時器叫 持續計時器。
擁塞控制
擁塞控制問題也是網絡中經常遇到的問題,面試的時候如果遇到擁塞控制問題那你可算有福了,然後你再答對了,那簡直就是妥妥滴加分項啊。擁塞控制問題,一般面試官不會問。不要問我爲什麼,因爲問十個同學九個不會,面試官也沒啥興致再問了。
不誇張的講,TCP中最複雜的問題就是擁塞控制。所以你要是看龍叔的文章,懂了,就是賺了。拿到offer記得給龍叔福報。
網絡擁塞就像生活中的城市堵車一樣,各方面如此發達的現代社會,交通堵塞問題還是一個頭疼的問題。到目前爲止還沒有一個能完全解決城市堵車問題的方案,目前所有的方法、政策、規則都是減輕、緩解堵塞問題,並沒有完全解決。交通堵塞問題的成因多,時效強,多變。網絡結構可是比交通結構複雜很多倍
通常我們衡量一個網絡的有效處理負荷的能力爲 吞吐量,把網絡中發送端輸入的負荷稱之爲 輸入負荷,理想情況下網絡的輸入負荷和吞吐量之間的關係是如下圖的關係。
理想情況下網絡的處理能力隨之輸入負荷的增大呈線性關係增大,到達網絡的最大處理能力時,吞吐量達到最大,此時不會隨着輸入負荷的增大而增大網絡吞吐量。
實際上網絡的處理能力是不會呈線性關係的,因爲網絡不會有理想狀況。就像我們的常說的人生一樣,理想很豐滿,現實很骨感。
看完上面的講解你不要以爲那就簡單控制輸入負荷就能解決網絡擁塞問題,可沒那麼簡單的。
網絡擁塞的本質就是 對網絡資源的需求大於可用資源 ,可用資源是有限的,因此網絡擁塞問題是一直存在的。
從原理上講,尋找擁塞控制的方案無非就是減少網絡資源的需求和增大可用資源,使得兩者能夠平衡。具體措施有 增大網絡的某些可用資源(如業務繁忙時增加一些鏈路,增大鏈路的帶寬,或使額外的通信量從另外的通路分流),或減少一些用戶對某些資源的需求
(如拒絕接受新的建立連接的請求,或要求用戶減輕其負荷,這屬於降低服務質量)。
但正如上面所講的,在採用某種措施時,還必須考慮到該措施所帶來的其他影響。
實踐證明,擁塞控制是很難設計的,因爲它是一個動態的(而不是靜態的)問題。
當前網絡正朝着高速化的方向發展,這很容易出現緩存不夠大而造成分組的丟
失。但分組的丟失是網絡發生擁塞的徵兆而不是原因。
在許多情況下,甚至正是擁塞控制機制本身成爲引起網絡性能惡化甚至發生死鎖的原因。這點應特別引起重視。
雖說網絡擁塞機制本身也有可能是恐怖分子,但大多數時候還是解決問題的關鍵。上面已經說清楚了網絡擁塞的原因和本質,下面就來分析下有那些方法可以避免和減少網絡擁塞。
目前解決網絡擁塞的方法主要有四種,慢開始、擁塞避免、快重傳、快恢復。
慢開始(慢啓動)
慢啓動可不是慢慢或者緩慢啓動喔,慢啓動指的是在網絡建立鏈接之後不立即發送最大數據段的數據,比如TCP最大數據段是1500字節,慢啓動會在建立連接之後第一次發送100個字節,收到確認後發送200字節,再次收到確認就發送300字節,依次增加直到達到最初設定的最大值。
唉,有人會說啦,前面流量控制講了發送窗口,這裏又有一個依次增加發送數據段大小,這麼說難道發送窗口控制流量沒用了? 別急嘛,龍叔給你說清楚咋回事。
在慢啓動方案設計時,大佬們也意識到這個問題。不能發送窗口大小是1000,慢啓動增加到1500字節了,這到底依誰的?
慢啓動會維持一個擁塞控制窗口叫做 擁塞窗口(CWND)。每次發送出去的字節大小會是兩者中的較小值。舉個例子,發送窗口可以發送1500字節,但是此時擁塞窗口是1000字節,最終發送出去的字節就是1000字節。
擁塞窗口大小也是變化的。TCP建立完成後會有一個初始化的擁塞窗口大小。初始化擁塞窗口大小爲當前TCP鏈接使用的最大數據段大小(Maximum Segment Size,MSS)
當發送一次數據後,在定時器過期之前收到了確認應答消息(ACK),則擁塞窗口大小變爲原來的2倍,依次往復,只要在定時器過期之前能收到ACK,都會增大2倍。一直到數據分組發生丟失,這個發生丟分組的點叫做 慢啓動閾值(Slow Start Threshold,SSTHRESH),初始化慢啓動閾值是64KB。
到達閾值後擁塞窗口大小衰減爲最初的大小即1MSSS大小,慢啓動閾值衰減爲原來閾值的一半。此後又是一個新的慢啓動過程。不過擁塞窗口(CWND)再次到達慢啓動閾值(SSTHRESH)之後,會啓動擁塞避免機制。
擁塞避免
擁塞避免是在慢啓動的基礎上的,當慢啓動的擁塞窗口第二次達到閾值的時候啓動擁塞避免,這也說明都第二次時網絡必然是有些問題存在了。
擁塞避免就是控制擁塞窗口增長速度。之前慢啓動是擁塞窗口是以當前窗口大小的二倍速度增長的,現在網絡有問題啦,不能再以這種速度增大擁塞窗口了,不然一直這樣繼續估計最後網絡可傳輸的字節就是,n趨近於無限大,可傳輸字節無限趨近於0。這就沒法玩了,就是死鎖了。
於是擁塞避免在第二次慢啓動到達閾值後就使擁塞窗口呈線性增長而不是指數增長。弄個圖看下具體過程
快重傳
快重傳就是一個字快,在一個TCP數據段發送出去之後會啓動一個定時器,看是否接受ACK會超時。然而快重傳就是解決這個只能通過定時器超時才能判斷數據是否丟失的機制,達到快速判斷的效果,快速判斷才能早點發送已經丟失的數據。節省時間,減少網絡阻塞。
什麼情況下才會觸發快重傳機制呢?可不是隨便一個數據段過來,就直接觸發快重傳了。
當接收端接收到不是按序到達的數據段時,此時接收端立即發送一個ACK響應報文,接收端接到三次重複ACK報文即可確認該數據段丟失,此時清零計時器,觸發重傳數據。
這一整個流程就是所謂的快重傳。快,沒話說吧。比你超時重傳快,根本不用等計時器超時。整個過程如圖所示。
快恢復
快恢復是在快重傳的基礎上的。當快重傳已經發送了丟失的數據後,快恢復機制被觸發。
在收到第三個重複ACK時,把當前CWND值設爲當前SSTHRESH值的一半,以減輕網絡負荷,然後執行前面介紹的“擁塞避免”算法,使CWND值慢慢增大,以避免再次出現網絡擁塞。
綜上我基本說完了,擁塞控制的全部內容,如果有不明白的地方可以私信龍叔一起學習。
我是龍叔,一個分享互聯網技術和心路歷程的大叔。支持我,記得給我點贊和分享。