通過故事引申網路協議TCP

VeCharm發表於2019-04-08
講一個故事。


三次握手

 long time ago,通訊並不發達,兩國之間的貿易卻絡繹不絕。但碼頭始終有限,如果貿然的把貨物運過去,如果這時候有別的商家已經佔領了碼頭。這樣不得不排隊了。後來,他們訓練了一種海鳥,運送之前,先用海鳥報個信: 

  1. 五弟,我要發一些貨過去。
  2. 收到,老地方,不見不散。

 一開始這個方式挺好用的,直到有一年夏天,五弟找上門來了:我等了你一天,你的貨呢?我交了錢借用了一天的碼頭,都白交了。二哥:我也著急呀,你一直沒回信,我都等了兩天,我不知道咋肥四,就沒發了。應該是回信的那隻鳥,被大風颳走了。最近海上天氣不好,想必我放的那隻鳥也是歷經千辛萬苦才去到你那。這樣吧,五弟,我改一下流程吧:

  1. 五弟,我要發一些貨過去。
  2. 收到,老地方,不見不散。
  3. 五弟,好的,老地方。

五弟琢磨了一下,前面的鳥能夠一來一回,二哥能看出海上沒有危險。我收到二哥的訊息後也能看出海上無風雨,這時我再去借碼頭,也能節省一些時間,妙,實在妙,還是二哥聰明,只是二哥要比我多訓練一倍的鳥。五弟哪裡的話,浪費這點資源算什麼呢。

三次握手就這樣誕生了。

流量控制

過了一些時日,五弟又來找二哥商量了:碼頭上的倉庫空間,人力始終有限,二哥一次派了這麼多艘船來,五弟實在忙不過來,船都堵在渡口,人家的船進不來,出不去啊。為了此事二哥想了一宿,找來了五弟商量:五弟呀,這個確實是個頭疼的問題呀。

最簡單的辦法就是一開始你就告訴我你能處理多少貨,我就派一批船過去。等你處理完了通知我,我再派一批過去。但是這種方式應變能力太低了,假設港口一開始沒這麼堵,但是可能還有一些商家在海上來往,結果我這一批派過去,就導致堵塞擁擠了。這個應變能力能理太差了,要提高這個應變能力,就不能派太多的船湧進港口,只能一小批,一小批的派了。

這樣你每次收到貨之後,就讓回來的船告訴我你那裡剩餘多少空間,我好安排下一趟船隻。五弟撓撓頭,不明白。二哥敲了下這木魚腦袋道:假設我們要運送720的貨,你碼頭的倉庫最多能存360的貨物,我每次派100的貨物,當然我的每艘船隻能載重是10(MTU 最大傳輸單元限制)。

  • 第一趟,我送過去100,我需要派10艘過去,你讓返回的船老大告訴我你那裡還剩260的空間。
  • 第二趟,我送過去100,我需要派10艘過去,你讓返回的船老大告訴我你那裡還剩160的空間。
  • 第三趟,我送過去100,我需要派10艘過去,你讓返回的船老大告訴我你那裡還剩60的空間。
  • 第四趟,我送過去60,我需要派6艘過去,這時候倉庫已滿,你開始讓工人搬運,等你搬運好了,就通知我。
  • 第五趟,收到五弟那邊的通知,我開始送過去100,我需要派10艘過去,你讓返回的船老大告訴我你那裡還剩260的空間。
  • 第六趟,我送過去100,我需要派10艘過去,你讓返回的船老大告訴我你那裡還剩160的空間。
  • 第七趟,我送過去100,我需要派10艘過去,你讓返回的船老大告訴我你那裡還剩60的空間。
  • 第八趟,我送過去60,我需要派6艘過去,這時候貨就發完了,圓滿大吉。

藉助視窗解決了流量控制的問題。

丟包重傳

沒過多久,五弟又來找二哥商量了:我收到的貨物缺斤少兩的,對不上數呀,我猜呀,海上風雲莫測,可能有些船隻失蹤了,這可咋辦呀。二哥想了想,能咋辦,重發唄。這樣,我給每一個船隻編上號,到了一段時間,沒回來的船隻,我再重發那批貨物吧。至於間隔多久才決定重發,這個非常重要。
  • 設長了,重發慢了,丟了老半天才發過去,效率太低了,也讓五弟等太久了。
  • 設短了,更恐怖,可能會導致沒有丟的也重發,這樣就重發多了,更多的船堵在海上,許久不能到達返回,導致更多的超時,更多的重發。

再加上海上風雲莫測,每次來回的時間不是固定的。只能動態的估算這個值了。這樣,每批次的船隻一來一回算一個單位(RTT)。然後根據這個RTT的時間計算超時時間(RTO)當發生了重發,就會將超時時間加倍。防止太頻繁重傳導致更加擁擠。

重傳歧異

但是這樣的會有一個問題,假設7號船一直沒回來,這時候再派一個7號船過去,許久後,終於回來了,那麼這艘船是第一艘的還是重發的那艘呢?忽略說能認出船老大的陳獨秀同學(摳鼻)。

  • 如果回來的船的是重發的那艘,假設是按第一艘來算,那麼間隔時間算長了。
  • 如果回來的船的是第一艘的,卻又按照重發的那艘來算,間隔時間又算短了。

這是一個苦差事,具體怎麼算,還是請位大師來算吧。此事算了了。

慢啟動

五弟又過了找二哥了:運720的貨都跑了8趟了,那送7200的貨那還不得九九八十一趟啊。這效率不行啊,二哥道:你想到的我也想到了,一開始我並不知道海上的擁擠情況,所以不敢派太多的船,我想了一個辦法,先發的慢一點,探測一下情況,然後再慢慢提速。假設我一開始派n艘船。

  • 先假定cwnd = 1,表示我這個批次派 cwnd*n 艘船。
  • 每當有一艘船回來,cwnd++,線性上升。
  • 每當過一個RTT,也就是當前批次的船回來了,cwnd = cwnd *2,指數上升。

這樣,就可以解決效率低的問題了。

擁塞避免

當然,不可能一直是增加。這樣擁堵只是時間問題。所以需要一個閾值。

  • 當 cwnd < 閾值,使用慢啟動演算法
  • 當cwnd >= 閾值,使用擁塞避免演算法

擁塞避免演算法的思路就是降低加速度,讓它緩慢的增長。

  • 每當有一艘船回來,cwnd = 1/cwnd
  • 每當過一個RTT,cwnd++

擁塞處理

以上都是正常的情況,當發生了重傳,說明發生了擁擠,就不能再發這麼多船了,這時候cwnd減半,重新進入慢啟動。

好了,故事就講到這裡了。

當出現了RTO超時重傳,因為ack都收不到了,所以它的處理會比較猛烈:

  • 將門限ssthresh的值設為當前cwnd值的一半,也就是閾值。之所以減半,是為了公平性,騰出空間給其他新連結保證頻寬。
  • cwnd設為1。
  • 重新進入慢啟動。

cwnd決定了傳送視窗的大小,如果只是出現了輕度的擁堵,就直接猛烈縮小視窗,進入慢啟動後,cwnd恢復的很慢,這顯然得不償失,所以需要一種主動觸發重傳的機制,就有了後來的快速重傳和快速恢復。

快速重傳

僅僅只是靠超時重傳,並不能發生了包丟失,卻要等到超時才開始重傳,這顯然效率不夠,所以需要找到重傳的規律。雖然我們發的包是有序的,但是IP包是無序的,所以會導致到達的包出現無序的情況。先來看下下面一組資料。

  • 1,2,3,4        已傳送,已確認到達
  • 5,6,7,8,     已傳送,未確到達
  • 9,10,11 ,12    在傳送緩衝區等待傳送,滑動視窗已經沒有空間了。

要保證可靠性,就必須要保證順序和完整,來看看事故發生現場。

  • 5,6,7,8。ack=9 , 因為Seq=5,length = 4,ack = seq+lenght,window size 剩餘 4。不會每一個都返回ack,這樣將會消耗大量的頻寬。
  • 6,5,7,8。ack=5,ack=5,ack=9,6到達的時候,發現順序不對,則會發出ack=5,期待客戶端下次傳送seq=5的包,因為還沒有達到三次所以客戶端不會理會這個資訊,果然後面就如期而至。
  •  6,7,8,5。ack=5,ack=5,ack=5,ack=5,ack=9。重複三次了,所以後面重發了5。

從上面的情況可以看出不管是亂序還是丟包,都會收到重複的ack的。在快速重傳演算法中,當收到三次重複的ack,就認定它是丟包了。但實際上,三次重複ack並不能判斷出是丟包或亂序,只能說出現了問題,它只是在效率和過早重傳之間的一個權衡。

  • 如果兩次重複ack就傳送可能會因為頻繁傳送導致擁擠堵塞。
  • 如果太晚傳送效率就跟不上。
  • 如果三次接收端都能收到重複ack,說明網路不會太差。

收到了三次重複的ack,進入快速重傳需要做幾件事情:

  • ssthresh =cwnd/2
  • cwnd=ssthresh
  • 這時候因為cwnd等於閾值,所以重新進入擁塞避免階段

由於快速重傳有可能是因為順序的問題而誤判,或者只是輕微的擁堵,導致進入了擁塞避免階段,這時候為了達到快速恢的目的,將會停止傳送後面的資料,只傳送重傳資料,為了提高恢復效率,將會使用快速恢演算法。

快速恢復

為了保證快速重傳效率,進入快速恢復階段會有以下幾個步驟。

  • 由於在上一個階段,cwnd和ssthresh變成了,ssthresh =cwnd/2,cwnd=ssthresh+3,之所以加3,是因為剛剛收到了3個重複的ack。
  • 當再次收到重複的ack,cwnd = cwnd+1。
  • 當收到新的ack,非重複的ack,cwnd=ssthresh,退出快速恢復階段,進入擁塞避免狀態。

總的來說就是騰出空間,快馬加鞭。

TCP 重傳機制

在上面的重傳原理介紹中,我們還忽視了一個問題,例如客戶端在傳送1,2,3,4,5,6,7包時,2,4,6,包丟了,這種情況還是比較常見的,這時候就會面臨一個問題,到底是一個一個重傳,還是將2後面的包都重發過去。

  • 停等協議。這時候接收方和傳送方的視窗大小都是1,這樣每次只能傳送一個,也就是一個一個重傳。缺點是效率太低。
  • 後退n幀協議。使用這個協議,為了保證接收方有足夠的空間,2之後的資料都要丟棄,然後傳送方將2後面的資料都全部傳送。缺點是需要有足夠大的快取空間。

    後退n幀協議雖然效率高,但是遇到大場面,在網路比較差或者頻寬比較低的情況,如果仍然使用這個協議會導致情況更加惡劣,這種情況停等協議雖然效率比較低,但是還是比較適用的。

通過故事引申網路協議TCP

視窗的滑動

TCP提供體積可變的滑動視窗機制,支援端對端的流量控制:

  • TCP連線階段,雙方協商視窗的尺寸,同時接收方預留資料快取區
  • 傳送方根據協商的結果,傳送符合視窗的尺寸的位元組流,然後等待ack確認。
  • 傳送方根據返回的確認資訊,調整視窗的尺寸,調整包括,如果出現傳送擁塞,傳送視窗縮小原來的一半,同時將超時重傳時間間隔擴大一倍,防止發生重傳風暴。

在傳輸的過程中,視窗是滑動的

看下圖5-15,TCP連線階段,雙方協商視窗的尺寸,同時接收方預留資料快取區

通過故事引申網路協議TCP

看圖5-16,假設視窗的閾值是一半,所以A傳送11個幀,傳送後,等待確認,傳送視窗位置不變。這時候發生了丟包,32,33沒有收到。雖然31已經收到了,視窗理論上可以往右移動一個位置,但是為了節省寬頻,不會每一個幀都回復ack,而是累積幾個之後在發出,所以31不會回覆ack,視窗就卡在這了。

通過故事引申網路協議TCP

看圖5-17,這時候30-33的已經確認了,所以視窗可以往右滑動三幀。37,38,40也許延遲還未收到。通過故事引申網路協議TCP

看圖5-18,在重傳機制生效之前,A繼續傳送了後面的資料,這時候視窗已經全部用完,A會停止傳送資料,等待B的視窗可用,B處理完之後會等待A傳送資料,這樣就造成了雙方在等待,為了解決這個問題,TCP使用了Zero Window Probe技術,縮寫ZWP。在視窗尺寸變成0之後,傳送端會傳送ZWP包詢問接收端是否有足夠的空間可以傳送資料了,TCP使用持續計時器,如果結果仍然為0,則重置定時器繼續等待。

丟包的原因

  • 接收方快取區滿了
  • 訊號差,波形失真
  • 包的校檢失敗
  • 網路擁塞

亂序的原因

  • IP包是無序的
  • 中介軟體的內部排程,如路由器,路由器裡面是有處理器,有些路由器使用多個流量處理單元,導致不同包在不同的處理單元,最後到達時間不一樣而發生了亂序
  • 多路複用,統一連結,不同路徑到達

TCP包頭格式

通過故事引申網路協議TCP

標誌位

  • SYN 用於連線請求時的標誌,SYN = 1
  • ACK 用於確認成功接收資料,ACK = 1
  • FIN  用於連線釋放的標誌,FIN= 1
  • RST 用於重置一個異常的連線,接收端 告知 傳送端 此連線有問題


  • PSH 用於通知接收端將快取區的資料提交給上層,即使沒有滿
  • URG 用於通知接收端當前為緊急資料,將這個資料直接提交給上層,它不經過快取區,後面的緊急指標表示緊急資料在當前資料的偏移位置

三次握手

通過故事引申網路協議TCP

  • 首先整個過程Seq都是動態生成的唯一序列號,無論誰發給誰,為了防止新連線和舊連線竄了。
  • ACK = 收到的Seq + 1
  • SYN = 1

連線過程的狀態

  • 客戶端SYN_SENT。客戶端傳送 SYN = 1,Seq = X 將狀態設定為 SYN_SENT
  • 服務端SYN_RCVD。半連線狀態,伺服器收到後將狀態設定為SYN_RCVD,並返回 SYN = 1,ACK= 1,Seq = Y,Ack = X + 1
  • 客戶端ESTABLISHED。客戶端收到後,狀態設為ESTABLISHED,表示連線已建立。併傳送ACK= Y + 1,Seq = Z
  • 服務端ESTABLISHED。服務端收到後,狀態設為ESTABLISHED,表示連線已建立。

伺服器維護了一個半連線的佇列,該佇列為每個客戶端的SYN包(SYN=X)開設一個條目,表示伺服器已經收到SYN包,並向客戶端發出確認,正在等待客戶端的確認包。當伺服器收到客戶端的確認包時,刪除該條目,該連線進入ESTABLISHED狀態。

四次揮手

通過故事引申網路協議TCP

  1. FIN-WAIT-1。客戶端停止傳送資料,FIN=1,Seq=X(前面已經傳送過來的資料最後一個位元組的序號+1),客戶端進入FIN-WAIT-1狀態。
  2. CLOSE-WAIT。伺服器發出Ack = X + 1,進入CLOSE-WAIT狀態,通知應用程式,因為伺服器是被動收到通知,此時服務可能資料還沒發完,還要繼續傳送。
  3. FIN-WAIT-2。收到伺服器的回覆,再次等待伺服器的關閉通知。這時候伺服器還可能發著資料給客戶端。
  4. LASK-ACK。伺服器終於把事情處理完了,可以發出FIN = 1,Seq = Z,ACK = X + 1告知伺服器斷開連線,進入最後確認狀態。
  5. TIME-WAIT,收到伺服器的關閉請求,客戶端回覆ACK = 1,Seq = X + 1,Ack = Z + 1,這時候客戶端不會馬上釋放連線,它會等待2MSL(報文最長存活時間),防止這個訊息伺服器沒有收到,而這時伺服器發出重傳資訊,客戶端就可以即時響應。
  6. CLOSED,伺服器收到訊息後釋放連線,客戶端經過2MSL後也釋放連線。

資料是如何傳輸的

兩臺電腦之間通訊都需要把數字訊號01轉化成電訊號,再調製成電磁波(比如光)。電磁波本身無法承載資訊的,但是我們可以控制電磁波的變化(幅度AM,頻率FM,相位PM),讓這些變化可以代表0和1。也就是說在訊號線中走的是電磁波,所以在佈線的時候要注意線與線之間的串擾。

電磁波有很多頻段比如2515MHz-2675MHz、4800MHz-4900MHz,在空氣中存在很多不同頻段的波,所以需要進行定寬調頻,也就是說確定頻寬,然後再進行濾波,所謂的濾波就是通過濾波演算法,比如卡爾曼濾波,根據截止頻率是否靠近特徵頻率以及衰減程度來分類的,最後轉換成01訊號。頻寬又叫頻寬是指在固定的的時間可傳輸的資料數量,亦即在傳輸管道中可以傳遞資料的能力。在數字裝置中,頻寬通常以 bps表示,即每秒可傳輸之位數。

截止頻率

用來說明電路頻率特性指標的特殊頻率。當保持電路輸入訊號的幅度不變,改變頻率使輸出訊號降至最大值的0.707倍,或某一特殊額定值時該頻率稱為截止頻率。在高頻端和低頻端各有一個截止頻率,分別稱為上截止頻率和下截止頻率。兩個截止頻率之間的頻率範圍稱為通頻帶。

將訊號轉化成位元組,再將位元組組裝成幀。再乙太網鏈路上的資料包被稱為以太幀,在802.3標準理,規定了一個以太幀的資料部分(Playload)的最大長度是1500個位元組(MTU),另外,乙太網幀最小長度為64位元組。

















相關文章