目錄
4.2 爲什麼建立連接是三次握手,而關閉連接卻是四次揮手呢?
4.3 爲什麼TIME_WAIT狀態需要經過2MSL(最大報文段生存時間)才能返回到CLOSE狀態?
當一臺計算機想要與另一臺計算機通訊時,兩臺計算機之間的通信需要暢通且可靠,這樣才能保證正確收發數據。例如,當你想查看網頁或查看電子郵件時,希望完整且按順序查看網頁,而不丟失任何內容。當你下載文件時,希望獲得的是完整的文件,而不僅僅是文件的一部分,因爲如果數據丟失或亂序,都不是你希望得到的結果,於是就用到了TCP。
TCP協議全稱是傳輸控制協議是一種面向連接的、可靠的、基於字節流的傳輸層通信協議,由 IETF 的RFC 793定義。TCP 是面向連接的、可靠的流協議。流就是指不間斷的數據結構,你可以把它想象成排水管中的水流。
因爲IP數據報中只有源IP和目的IP地址,沒有端口號,所以我們就需要引入TCP協議來增加對端口號的支持,滿足計算機中不同進程之間的通信。
一、TCP報文格式
1.1 標誌位
- URG:指示報文中有緊急數據,應儘快傳送(相當於高優先級的數據)。
- PSH:爲1表示是帶有push標誌的數據,指示接收方在接收到該報文段以後,應儘快將這個報文段交給應用程序,而不是在緩衝區排隊。
- RST:TCP連接中出現嚴重差錯(如主機崩潰),必須釋放連接,在重新建立連接。
- FIN:發送端已完成數據傳輸,請求釋放連接。
- SYN:處於TCP連接建立過程。 (Synchronize Sequence Numbers),請求建立連接
- ACK:確認序號標誌,爲1時表示確認號有效,爲0表示報文中不含確認信息,忽略確認號字段。
1.2 窗口
滑動窗口大小,這個字段是接收端用來告知發送端自己還有多少緩衝區可以接受數據。於是發送端可以根據這個接收端的處理能力來發送數據,而不會導致接收端處理不過來。(以此控制發送端發送數據的速率,從而實現流量控制)窗口大小是一個16bit字段,因而窗口大小最大爲65535。
1.3 頭部長度(首部長度)
由於TCP首部包含一個長度可變的選項和填充部分,所以需要這麼一個值來指定這個TCP報文段到底有多長。或者可以這麼理解:就是表示TCP報文段中數據部分在整個TCP報文段中的位置。該字段的單位是32位字,即:4個字節。TCP的滑動窗口大小實際上就是socket的接收緩衝區大小的字節數。
1.4 選項和填充部分
TCP報文的字段實現了TCP的功能,標識進程、對字節流拆分組裝、差錯控制、流量控制、建立和釋放連接等。其最大長度可根據TCP首部長度進行推算。TCP首部長度用4位表示,那麼選項部分最長爲:(2^4-1)*(32/8)-20=40字節。
1.5 校驗和
佔2字節。檢驗和的字段檢驗的範圍包括首部和數據這兩部分。和UDP 用戶數據報一樣,在計算檢驗和時,要在TCP報文段的前面加上12字節的僞首部。僞首部的格式與UDP用戶數據報的僞首部一樣。但應把僞首部第4個字段中的17改爲6 (TCP的協議號是6),把第5字段中的UDP長度改爲TCP長度。接收方收到此報文段後,仍要加上這個僞首部來計算檢驗和。TCP校驗方法和UDP一樣。若使用IPv6,則相應的僞首部也要改變。
二、TCP協議的特點
2.1 面向連接
面向連接,是指發送數據之前必須在兩端建立連接。建立連接的方法是“三次握手”,這樣能建立可靠的連接。建立連接,是爲數據的可靠傳輸打下了基礎。
2.2 僅支持單播傳輸
每條TCP傳輸連接只能有兩個端點,只能進行點對點的數據傳輸,不支持多播和廣播傳輸方式。
2.3 面向字節流
TCP不像UDP一樣那樣一個個報文獨立地傳輸,而是在不保留報文邊界的情況下以字節流方式進行傳輸。
創建一個TCP的socket, 同時在內核中創建一個發送緩衝區和一個接收緩衝區;
另一方面, TCP的一個連接, 既有發送緩衝區, 也有接收緩衝區, 那麼對於這一個連接, 既可以讀數據, 也可以寫數據. 這個概念叫做全雙工 。
- 調用write時, 數據會先寫入發送緩衝區中;
- 如果發送的字節數太長, 會被拆分成多個TCP的數據包發出; 如果發送的字節數太短, 就會先在緩衝區裏等待, 等到緩衝區長度差不多了, 或者其他合適的時機發送出去;
- 接收數據的時候, 數據也是從網卡驅動程序到達內核的接收緩衝區;
- 然後應用程序可以調用read從接收緩衝區拿數據;
2.4 可靠傳輸
對於可靠傳輸,判斷丟包,誤碼靠的是TCP的段編號以及確認號。TCP爲了保證報文傳輸的可靠,就給每個包一個序號,同時序號也保證了傳送到接收端實體的包的按序接收。然後接收端實體對已成功收到的字節發回一個相應的確認(ACK);如果發送端實體在合理的往返時延(RTT)內未收到確認,那麼對應的數據(假設丟失了)將會被重傳。
2.5 提供擁塞控制
當網絡出現擁塞的時候,TCP能夠減小向網絡注入數據的速率和數量,緩解擁塞
2.6 TCP提供全雙工通信
TCP允許通信雙方的應用程序在任何時候都能發送數據,因爲TCP連接的兩端都設有緩存,用來臨時存放雙向通信的數據。當然,TCP可以立即發送一個數據段,也可以緩存一段時間以便一次發送更多的數據段(最大的數據段大小取決於MSS)
三、 TCP連接過程(三次握手)
3.1 TCP三次握手
所謂三次握手(Three-Way Handshake)即建立TCP連接,就是指建立一個TCP連接時,需要客戶端和服務端總共發送3個包以確認連接的建立。在socket編程中,這一過程由客戶端執行connect來觸發,整個流程如下圖所示:
最開始的時候客戶端和服務器都是處於CLOSED狀態。主動打開連接的爲客戶端,被動打開連接的是服務器。
- TCP服務器進程先創建傳輸控制塊TCB,時刻準備接受客戶進程的連接請求,此時服務器就進入了LISTEN(監聽)狀態;
- 第一次握手:TCP客戶進程也是先創建傳輸控制塊TCB,然後向服務器發出連接請求報文,此時報文首部中的同步位SYN=1,同時隨機產生一個初始序列號 seq=x ,並將該數據包發送給TCP服務端。此時,TCP客戶端進程進入了 SYN-SENT(同步已發送狀態)狀態。TCP規定,SYN報文段(SYN=1的報文段)不能攜帶數據,但需要消耗掉一個序號。
- 第二次握手:TCP服務器收到請求報文後,由標誌位SYN=1知道了TCP客戶端是想要請求建立連接,如果同意連接,則發出確認報文。確認報文中應該 ACK=1,SYN=1,確認號是ack=x+1,同時也要爲自己隨機初始化一個序列號 seq=y,此時,TCP服務器進程進入了SYN-RCVD(同步收到)狀態。這個報文也不能攜帶數據,但是同樣要消耗一個序號。
- 第三次握手:TCP客戶進程收到確認後,檢查ack是否等於x+1,如果是等於1則說明該數據包是TCP服務端發過來的確認連接數據包。此時客戶端還要向服務器給出確認報文。確認報文的ACK=1,ack=y+1,自己的序列號seq=x+1,此時,TCP連接建立,客戶端進入ESTABLISHED(已建立連接)狀態。TCP規定,ACK報文段可以攜帶數據,但是如果不攜帶數據則不消耗序號。
- 當服務器收到客戶端的確認後,檢查ack是否爲y+1,ACK是否爲1,如果正確則連接建立成功,服務器也進入ESTABLISHED狀態,此後雙方就可以開始通信了。
簡單來說,三次握手過程如下:
第一次握手
客戶端向服務端發送連接請求報文段(SYN包)。該報文段中包含自身的數據通訊初始序號(seq)。請求發送後,客戶端便進入 SYN-SENT 狀態。
第二次握手
服務端收到連接請求報文段(SYN包)後,如果同意連接,則會發送一個應答包(SYN+ACK包),該應答中也會包含自身的數據通訊初始序號(seq),發送完成後便進入 SYN-RECEIVED 狀態。
第三次握手
當客戶端收到連接同意的應答包(SYN+ACK包)後,還要向服務端發送一個確認報文(ACK包)。客戶端發完這個報文段後便進入 ESTABLISHED 狀態,服務端收到這個應答後也進入 ESTABLISHED 狀態,此時連接建立成功,以後就可以開始傳輸數據了。
類比:
A:您好,我是 A
B:您好 A,我是 B
A:您好 B
三次握手主要目的是:信息對等和防止超時。防止超時導致髒連接。除了建立連接外,主要還是爲了溝通 TCP 包的序號問題。
3.2 爲什麼不用兩次握手?
主要是爲了防止已經失效的連接請求報文突然又傳送到了服務器,從而產生錯誤。如果使用的是兩次握手建立連接,假設有這樣一種場景,客戶端發送的第一個請求連接並且沒有丟失,只是因爲在網絡中滯留的時間太長了,由於TCP的客戶端遲遲沒有收到確認報文,以爲服務器沒有收到,此時重新向服務器發送這條報文,此後客戶端和服務器經過兩次握手完成連接,傳輸數據,然後關閉連接。此時之前滯留的那一次請求連接,因爲網絡通暢了, 到達了服務器,這個報文本該是失效的,但是,兩次握手的機制將會讓客戶端和服務器再次建立連接,這將導致不必要的錯誤和資源的費。
如果採用的是三次握手,就算是那一次失效的報文傳送過來了,服務端接受到了那條失效報文並且回覆了確認報文,但是客戶端不會再次發出確認。由於服務器收不到確認,就知道客戶端並沒有請求連接。
3.3 爲什麼不用四次握手?
因爲三次已經可以滿足需要了, 四次就多餘了。
3.4 DOS攻擊
DOS攻擊利用合理的服務請求佔用過多的服務資源,使正常用戶的請求無法得到相應。
常見的DOS攻擊有計算機網絡帶寬攻擊和連通性攻擊。
- 帶寬攻擊指以極大的通信量衝擊網絡,使得所有可用網絡資源都被消耗殆盡,最後導致合法的用戶請求無法通過。
- 連通性攻擊指用大量的連接請求衝擊計算機,使得所有可用的操作系統資源都被消耗殆盡,最終計算機無法再處理合法用戶的請求。
3.5 SYN攻擊
SYN洪水攻擊屬於DOS攻擊的一種,它利用TCP協議缺陷,通過發送大量的半連接請求,耗費CPU和內存資源。
在三次握手過程中,Server發送SYN-ACK之後,收到Client的ACK之前的TCP連接稱爲半連接(half-open connect),此時Server處於SYN_RCVD狀態,當收到ACK後,Server轉入ESTABLISHED狀態。SYN攻擊就是Client在短時間內僞造大量不存在的IP地址,並向Server不斷地發送SYN包,Server回覆確認包,並等待Client的確認,由於源地址是不存在的,因此,Server需要不斷重發直至超時,這些僞造的SYN包將產時間佔用未連接隊列,導致正常的SYN請求因爲隊列滿而被丟棄,從而引起網絡堵塞甚至系統癱瘓。SYN攻擊時一種典型的DOS攻擊,檢測SYN攻擊的方式非常簡單,即當Server上有大量半連接狀態且源IP地址是隨機的,則可以斷定遭到SYN攻擊了,使用如下命令可以讓之現行:
#netstat -nap | grep SYN_RECV
四、 TCP斷開鏈接(四次揮手)
4.1 TCP四次揮手
所謂四次揮手(Four-Way Wavehand)即終止TCP連接,就是指斷開一個TCP連接時,需要客戶端和服務端總共發送4個包以確認連接的斷開。在socket編程中,這一過程由客戶端或服務端任一方執行close來觸發,整個流程如下圖所示:
數據傳輸完畢後,雙方都可釋放連接。最開始的時候,客戶端和服務器都是處於ESTABLISHED狀態,然後客戶端主動關閉,服務器被動關閉。
- 第一次揮手:客戶端進程發出連接釋放報文,並且停止發送數據。釋放數據報文首部,FIN=1,其序列號爲seq=u(等於前面已經傳送過來的數據的最後一個字節的序號加1),此時,客戶端進入FIN-WAIT-1(終止等待1)狀態。 TCP規定,FIN報文段即使不攜帶數據,也要消耗一個序號。
- 第二次揮手:服務器收到連接釋放報文,發出確認報文,ACK=1,ack=u+1,並且帶上自己的序列號seq=v,此時,服務端就進入了CLOSE-WAIT(關閉等待)狀態。TCP服務器通知高層的應用進程,客戶端向服務器的方向就釋放了,這時候處於半關閉狀態,即客戶端已經沒有數據要發送了,但是服務器若發送數據,客戶端依然要接受。這個狀態還要持續一段時間,也就是整個CLOSE-WAIT狀態持續的時間。
- 客戶端收到服務器的確認請求後,校驗確認請求的ack是否等於u+1,如果等於,則此時客戶端就進入FIN-WAIT-2(終止等待2)狀態,等待服務器發送連接釋放報文(在這之前還需要接受服務器發送的最後的數據)。
- 第三次揮手:服務器將最後的數據發送完畢後,就向客戶端發送連接釋放報文,FIN=1,ack=u+1,由於在半關閉狀態,服務器很可能又發送了一些數據,假定此時的序列號爲seq=w,此時,服務器就進入了LAST-ACK(最後確認)狀態,等待客戶端的確認。
- 第四次揮手:客戶端收到服務器的連接釋放報文後,必鬚髮出確認,ACK=1,ack=w+1,而自己的序列號是seq=u+1,此時,客戶端就進入了TIME-WAIT(時間等待)狀態。注意此時客戶端的TCP連接還沒有釋放,必須經過2MSL(最長報文段壽命)的時間後,當客戶端撤銷相應的TCB後,才進入CLOSED狀態。
- 服務器只要收到了客戶端發出的確認,確認ack等於w+1後,則立即進入CLOSED狀態。同樣,撤銷TCB後,就結束了這次的TCP連接。可以看到,服務器結束TCP連接的時間要比客戶端早一些。
解釋一下這兩種狀態:
- TIME_WAIT:主動要求關閉的機器表示收到了對方的FIN報文,併發送出了ACK報文,進入TIME_WAIT狀態,等2MSL後即可進入到CLOSED狀態。如果FIN_WAIT_1狀態下,同時收到待FIN標識和ACK標識的報文時,可以直接進入TIME_WAIT狀態,而無需經過FIN_WAIT_2狀態。
- CLOSE_WAIT:被動關閉的機器收到對方請求關閉連接的FIN報文,在第一次ACK應答後,馬上進入CLOSE_WAIT狀態。這種狀態其實標識在等待關閉,並且通知應用發送剩餘數據,處理現場信息,關閉相關資源。
簡單來說,四次揮手的過程如下:
TCP 是全雙工的,在斷開連接時兩端都需要發送 FIN包 和 ACK包。
第一次揮手
若客戶端 A 認爲數據發送完成,則它需要向服務端 B 發送連接釋放請求(FIN包),進入FIN_WAIT_1狀態。
第二次揮手
服務端B 收到連接釋放請求(FIN包)後,會告訴應用層要釋放 TCP 鏈接。然後會發送應答報文(ACK包),並進入 CLOSE_WAIT 狀態,此時表明 A 到 B 的連接已經釋放,不再接收 A 發的數據了。但是因爲 TCP 連接是雙向的,所以 B 仍舊可以發送數據給 A。
第三次揮手
服務端B 如果此時還有沒發完的數據會繼續發送,完畢後會向 客戶端A 發送連接釋放請求(FIN包),然後 B 便進入 LAST-ACK 狀態。
第四次揮手
客戶端A 收到連接釋放請求(FIN包)後,向 B 發送確認應答(ACK包),此時 A 進入 TIME-WAIT 狀態。該狀態會持續 2MSL(最大段生存期,指報文段在網絡中生存的時間,超時會被拋棄) 時間,若該時間段內沒有 B 的重發請求的話,就進入 CLOSED 狀態。當 服務端B 收到確認應答後,會立即進入 CLOSED 狀態。
類比:
A:B 啊,我不想玩了
B:哦,你不想玩了啊,我知道了
這個時候,只是 A 不想玩了,即不再發送數據,但是 B 可能還有未發送完的數據,所以需要等待 B 也主動關閉。
B:A 啊,好吧,我也不玩了,拜拜
A:好的,拜拜
4.2 爲什麼建立連接是三次握手,而關閉連接卻是四次揮手呢?
這是因爲服務端在LISTEN狀態下,收到建立連接請求的SYN報文後,把ACK和SYN放在一個報文裏發送給客戶端。而關閉連接時,當收到客戶端的FIN報文時,僅僅表示對方不再發送數據了但是還能接收數據,但服務端也未必全部數據都發送給客戶端了,所以客戶端既可以立即close,也可以發送一些數據給對方後,再發送FIN報文給對方來表示同意現在關閉連接,因此,服務端ACK和FIN一般都會分開發送。所以就會有四次揮手,比建立連接的時候多一次操作。
4.3 爲什麼TIME_WAIT狀態需要經過2MSL(最大報文段生存時間)才能返回到CLOSE狀態?
原因有二:
一、保證TCP協議的全雙工連接能夠可靠關閉
二、保證這次連接的重複數據段從網絡中消失
先說第一點,如果Client直接CLOSED了,那麼由於IP協議的不可靠性或者是其它網絡原因,導致Server沒有收到Client最後回覆的ACK。那麼Server就會在超時之後繼續發送FIN,此時由於Client已經CLOSED了,就找不到與重發的FIN對應的連接,最後Server就會收到RST而不是ACK,Server就會以爲是連接錯誤把問題報告給高層。這樣的情況雖然不會造成數據丟失,但是卻導致TCP協議不符合可靠連接的要求。所以,Client不是直接進入CLOSED,而是要保持TIME_WAIT,這樣如何Server沒有收到Client的ACK的話,就會重新向Client發送一個FIN,當Client再次收到FIN的時候,能夠再發送一個ACK,保證Server收到ACK,最後正確的關閉連接。
再說第二點,如果Client直接CLOSED,然後又再向Server發起一個新連接,我們不能保證這個新連接與剛關閉的連接的端口號是不同的。也就是說有可能新連接和老連接的端口號是相同的。一般來說不會發生什麼問題,但是還是有特殊情況出現:假設新連接和已經關閉的老連接端口號是一樣的,如果前一次連接的某些數據仍然滯留在網絡中,這些延遲數據在建立新連接之後纔到達Server,由於新連接和老連接的端口號是一樣的,又因爲TCP協議判斷不同連接的依據是socket pair,於是,TCP協議就認爲那個延遲的數據是屬於新連接的,這樣就和真正的新連接的數據包發生混淆了。所以TCP連接還要在TIME_WAIT狀態等待2倍MSL,這樣可以保證本次連接的所有數據都從網絡中消失。
4.4 如果已經建立了連接,但是客戶端突然出現故障了怎麼辦?
TCP設有一個保活計時器,顯然,客戶端如果出現故障,服務器不能一直等下去,白白浪費資源。服務器每收到一次客戶端的請求後都會重新復位這個計時器,時間通常是設置爲2小時,若兩小時還沒有收到客戶端的任何數據,服務器就會發送一個探測報文段,以後每隔75分鐘發送一次。若一連發送10個探測報文仍然沒反應,服務器就認爲客戶端出了故障,接着就關閉連接。
五、滑動窗口
TCP滑動窗口技術通過動態改變窗口大小來調節兩臺主機間數據傳輸。每個TCP/IP主機支持全雙工數據傳輸,因此TCP有兩個滑動窗口:一個用於接收數據,另一個用於發送數據。TCP使用肯定確認技術,其發送的確認號(ack)指的是它所期待下一個要接收的報文的序號(seq)。 假定發送方設備以每一次三個數據包的方式發送數據,也就是說,窗口大小爲3。發送方發送序列號爲1、2、3的三個數據包,接收方設備成功接收數據包,用序列號4確認。發送方設備收到確認,繼續以窗口大小3發送數據。當接收方設備要求降低或者增大網絡流量時,可以對窗口大小進行減小或者增加,本例降低窗口大小爲2,每一次發送兩個數據包。當接收方設備要求窗口大小爲0,表明接收方已經接收了全部數據,或者接收方應用程序沒有時間讀取數據,要求暫停發送。發送方接收到攜帶窗口號爲0的確認,停止這一方向的數據傳輸。當鏈路變好了或者變差了這個窗口還會發生變化,並不是第一次協商好了以後就永遠不變了。
滑動窗口協議,是TCP使用的一種流量控制方法。該協議允許發送方在停止並等待確認前可以連續發送多個分組。由於發送方不必每發一個分組就停下來等待確認,因此該協議可以加速數據的傳輸。 只有在接收窗口向前滑動時(與此同時也發送了確認),發送窗口才有可能向前滑動。收發兩端的窗口按照以上規律不斷地向前滑動,因此這種協議又稱爲滑動窗口協議。
流量控制:端到端,接收端的應用層處理速度決定和網速無關,由接收端返回的rwnd控制
- cwnd:發送端窗口( congestion window )
- rwnd:接收端窗口(receiver window)
六、擁塞控制
6.1 擁塞控制
發送端主動控制cwnd,有慢啓動(從cwnd初始爲1開始啓動,指數啓動),擁塞避免(到達ssthresh後,爲了避免擁塞開始嘗試線性增長),快重傳(接收方每收到一個報文段都要回復一個當前最大連續位置的確認,發送方只要一連收到三個重複確認就知道接收方丟包了,快速重傳丟包的報文,並TCP馬上把擁塞窗口 cwnd 減小到1),快恢復(直接從ssthresh線性增長)。
如果網絡上的延時突然增加,那麼TCP對這個事作出的應對只有重傳數據,但是重傳會導致網絡的負擔更重,於是會導致更大的延遲以及更多的丟包,於是這個情況就會進入惡性循環被不斷地放大。試想一下,如果一個網絡內有成千上萬的TCP連接都這麼行事,那麼馬上就會形成“網絡風暴”,TCP這個協議就會拖垮整個網絡。所以TCP不能忽略網絡上發生的事情,而無腦地一個勁地重發數據,對網絡造成更大的傷害。對此TCP的設計理念是:TCP不是一個自私的協議,當擁塞發生的時候,要做自我犧牲。就像交通阻塞一樣,每個車都應該把路讓出來,而不要再去搶路了。
6.2 慢啓動
只有在TCP連接建立和網絡出現超時時才使用。每經過一個傳輸輪次,擁塞窗口 cwnd 就加倍。一個傳輸輪次所經歷的時間其實就是往返時間RTT。不過“傳輸輪次”更加強調:把擁塞窗口cwnd所允許發送的報文段都連續發送出去,並收到了對已發送的最後一個字節的確認。另外,慢開始的“慢”並不是指cwnd的增長速率慢,而是指在TCP開始發送報文段時先設置cwnd=1,使得發送方在開始時只發送一個報文段(目的是試探一下網絡的擁塞情況),然後再逐漸增大cwnd。
爲了防止擁塞窗口cwnd增長過大引起網絡擁塞,還需要設置一個慢開始門限ssthresh狀態變量(如何設置ssthresh)。慢開始門限ssthresh的用法如下:
- 當 cwnd < ssthresh 時,使用上述的慢開始算法。
- 當 cwnd > ssthresh 時,停止使用慢開始算法而改用擁塞避免算法。
- 當 cwnd = ssthresh 時,既可使用慢開始算法,也可使用擁塞控制避免算法。
擁塞避免算法:讓擁塞窗口cwnd緩慢地增大,即每經過一個往返時間RTT就把發送方的擁塞窗口cwnd加1,而不是加倍。這樣擁塞窗口cwnd按線性規律緩慢增長,比慢開始算法的擁塞窗口增長速率緩慢得多。
強調:“擁塞避免”並非指完全能夠避免了擁塞。利用以上的措施要完全避免網絡擁塞還是不可能的。“擁塞避免”是說在擁塞避免階段將擁塞窗口控制爲按線性規律增長,使網絡比較不容易出現擁塞。
七、差錯控制
TCP使用差錯控制來提供可靠性。差錯控制包括以下的一些機制:檢測和重傳受到損傷的報文段、重傳丟失的報文段、保存失序到達的報文段直至缺失的報文到期,以及檢測和丟棄重複的報文段。TCP通過三個簡單的工具來完成其差錯控制:檢驗和、選擇確認(SACK)以及超時重傳。
八、TCP如何保證可靠傳輸
主要通過以下技術:
8.1 確認應答機制&序列號
TCP將每個字節的數據都進行了編號,即爲序列號。
每一個ACK都帶有對應的確認序列號,意思是告訴發送者,我已經收到了哪些數據;;下一次你從哪裏開始發。
8.2 超時重傳&序列號
主機A發送數據給B之後, 可能因爲網絡擁堵等原因, 數據無法到達主機B; 如果主機A在一個特定時間間隔內沒有收到B發來的確認應答, 就會進行重發;
主機A未收到B發來的確認應答,也可能是因爲ACK丟失了,因此主機B會收到很多重複數據.。那麼TCP協議需要能夠識別出那些包是重複的包,,並且把重複的丟棄掉.,這時候我們可以利用序列號, 就可以很容易做到去重的效果。
8.3 擁塞控制
每次發送數據包的時候, 將擁塞窗口和接收端主機反饋的窗口大小做比較, 取較小的值作爲實際發送的窗口。
擁塞控制, 歸根結底是TCP協議想盡可能快的把數據傳輸給對方, 但是又要避免給網絡造成太大壓力的折中方案。
九、提高傳輸效率
有這四種方法:滑動窗口、流量控制、延遲應答、捎帶應答
9.1 滑動窗口機制
- 窗口大小指的是無需等待確認應答而可以繼續發送數據的最大值.
- 發送窗口內字段的時候, 不需要等待任何ACK, 直接發送;
- 收到第一個ACK後, 滑動窗口向後移動, 繼續發送下一個窗口字段的數據; 依次類推;
- 操作系統內核爲了維護這個滑動窗口, 需要開闢發送緩衝區來記錄當前還有哪些數據沒有應答; 只有確認應答過的數據, 才能從緩衝區刪掉;
- 窗口越大, 則網絡的吞吐率就越高
9.2 流量控制
接收端處理數據的速度是有限的. 如果發送端發的太快, 導致接收端的緩衝區被打滿, 這個時候如果發送端繼續發送, 就會造成丟包, 繼而引起丟包重傳等等一系列連鎖反應。
- 接收端將自己可以接收的緩衝區大小放入TCP首部中的 "窗口大小" 字段, 通過ACK端通知發送端;
- 窗口大小字段越大, 說明網絡的吞吐量越⾼高;
- 接收端一旦發現自己的緩衝區快滿了, 就會將窗口大小設置成一個更小的值通知給發送端;
- 發送端接受到這個窗口之後, 就會減慢自己的發送速度;
- 如果接收端緩衝區滿了, 就會將窗口置爲0; 這時發送⽅方不再發送數據, 但是需要定期發送一個窗口
9.3 延遲應答
如果接收數據的主機立刻返回ACK應答, 這時候返回的窗口可能比較小.
窗口越大, 網絡吞吐量就越大, 傳輸效率就越高. 我們的目標是在保證網絡不擁塞的情況下儘量提高傳輸效率;
9.4 捎帶應答
在延遲應答的基礎上, 我們發現, 很多情況下, 客戶端服務器在應用層也是 “一發一收” 的.
意味着客戶端給服務器說了 “How are you”, 服務器也會給客戶端回一個 “Fine, thank you”; 那麼這個時候ACK就可以搭順風車, 和服務器迴應的 “Fine, thank you” 一起回給客戶端。
十、TCP粘包問題
10.1 TCP粘包問題
- 首先要明確, 粘包問題中的 “包” , 是指的應用層的數據包;
- 在TCP的協議頭中, 沒有如同UDP一樣的“報文長度”這樣的字段, 但是有一個序號這樣的字段;
- 站在傳輸層的角度, TCP是一個一個報文過來的,按照序號排好序放在緩衝區中;
- 站在應用層的角度, 看到的只是一串連續的字節數據. 那麼應用程序看到了這麼一連串的字節數據, 就不知道從哪個部分開始到哪個部分是一個完整的應用層數據包。
10.2 如何避免粘包問題?
歸根結底就是一句話, 明確兩個包之間的邊界.
- 對於定長的包, 保證每次都按固定大小讀取即可;
- 對於變長的包, 可以在報頭的位置, 約定一個包總長度的字段, 從而就知道了包的結束位置;
- 對於變長的包, 還可以在包和包之間使用明確的分隔符。
- TLV格式的數據傳輸
十一、總結及常見面試問題
11.1 TCP 和 UDP 的區別
|
UDP |
TCP |
是否連接 |
無連接 |
面向連接 |
是否可靠 |
不可靠傳輸,不使用流量控制和擁塞控制。不保證數據順序。可能會丟包 |
可靠傳輸,使用流量控制和擁塞控制。保證數據順序。保證數據正確性 |
連接對象個數 |
支持一對一,一對多,多對一和多對多交互通信 |
只能是一對一通信 |
傳輸方式 |
面向報文 |
面向字節流 |
首部開銷 |
首部開銷小,僅8字節,結構較簡單 |
首部最小20字節,最大60字節,開銷大 |
適用場景 |
適用於實時應用(IP電話、視頻會議、直播等) |
適用於要求可靠傳輸的應用,例如文件傳輸 |
11.2 什麼是面向連接,什麼是面向無連接
在互通之前,面向連接的協議會先建立連接,如 TCP 有三次握手,而 UDP 不會
11.3 TCP 爲什麼是可靠連接
- 通過 TCP 連接傳輸的數據無差錯,不丟失,不重複,且按順序到達。
- TCP 報文頭裏面的序號能使 TCP 的數據按序到達
- 報文頭裏面的確認序號能保證不丟包,並且有累計確認及超時重傳機制
- TCP 擁有流量控制及擁塞控制的機制
相關文章:【計算機網絡】一篇文章帶你快速掌握UDP協議
【計算機網絡】一篇文章快速瞭解Socket
參考鏈接:https://blog.csdn.net/u013256816/article/details/84001583
https://blog.csdn.net/sifanchao/article/details/82285018
計算機網絡(第7版)-謝希仁