linuxtcp圖解

- tcp頭部(20-60字節)

- TCP端口號
TCP的連接是需要四個要素確定唯一一個連接:
(源IP,源端口號)+ (目地IP,目的端口號)
所以TCP首部預留了兩個16位作為端口號的存儲,而IP地址由上一層IP協議負責傳遞
源端口號和目地端口各占16位兩個字節,也就是端口的范圍是2^16=65535
另外1024以下是系統保留的,從1024-65535是用戶使用的端口范圍 - TCP的序號和確認號:
32位序號 seq:Sequence number 縮寫seq ,TCP通信過程中某一個傳輸方向上的字節流的每個字節的序號,通過這個來確認發送的數據有序,比如現在序列號為1000,發送了1000,下一個序列號就是2000。
32位確認號 ack:Acknowledge number 縮寫ack,TCP對上一次seq序號做出的確認號,用來響應TCP報文段,給收到的TCP報文段的序號seq加1。 - TCP的標志位
每個TCP段都有一個目的,這是借助於TCP標志位選項來確定的,允許發送方或接收方指定哪些標志應該被使用,以便段被另一端正確處理。
用的最廣泛的標志是 SYN,ACK 和 FIN,用於建立連接,確認成功的段傳輸,最后終止連接。- SYN:簡寫為
S,同步標志位,用於建立會話連接,同步序列號; - ACK: 簡寫為
.,確認標志位,對已接收的數據包進行確認; - FIN: 簡寫為
F,完成標志位,表示我已經沒有數據要發送了,即將關閉連接; - PSH:簡寫為
P,推送標志位,表示該數據包被對方接收后應立即交給上層應用,而不在緩沖區排隊; - RST:簡寫為
R,重置標志位,用於連接復位、拒絕錯誤和非法的數據包; - URG:簡寫為
U,緊急標志位,表示數據包的緊急指針域有效,用來保證連接不被阻斷,並督促中間設備盡快處理;
- SYN:簡寫為
TCP三次握手

-
客戶端向服務器發來請求,SYN為1(表示建立連接),seq隨機生成一個序列號J,客戶端進入SYN_SENT狀態。
-
服務器在收到請求時,發出應答包,SYN=1,ACK=1(表示已接受到數據包),返回確認號ack=J+1,生成一個序列號seq=K
-
客戶端收到數據包,判斷ack=J+1,ACK=1是否正確,正確返回ACK為1,ack為K+1的數據包,進入
ESTABLISHED(已連接)。服務器判斷發來數據包是否正確,然后也進入ESTABLISHED狀態
TCP四次揮手
兩端都可以發送斷開請求,以客戶端為例

- 客戶端發送一個FIN=M(finish請求斷開),進入FIN_WAIT1狀態
- 服務器收到斷開請求,發一個ack=M+1,等待自己確認服務端發送完數據,進入CLOSE_WAIT,客戶端收到進入FIN_WAIT2
- 服務端發送完數據,發一個FIN=N,進入LAST_WAIT
- 客戶端收到之后,進入TIME_WAIT狀態,發一個ack = N+1,ACK = 1.服務端接收到斷開連接,客戶端等待2MSL后沒有收到數據,就斷開連接,(MSL是數據包在網路中失效的最大時間單位)
為什么要等待2MSL?
有以下兩個原因:
- 第一點:保證TCP協議的全雙工連接能夠可靠關閉:
由於IP協議的不可靠性或者是其它網絡原因,導致了Server端沒有收到Client端的ACK報文,那么Server端就會在超時之后重新發送FIN,如果此時Client端的連接已經關閉處於CLOESD狀態,那么重發的FIN就找不到對應的連接了,從而導致連接錯亂,所以,Client端發送完最后的ACK不能直接進入CLOSED狀態,而要保持TIME_WAIT,當再次收到FIN的收,能夠保證對方收到ACK,最后正確關閉連接。 - 第二點:保證這次連接的重復數據段從網絡中消失
如果Client端發送最后的ACK直接進入CLOSED狀態,然后又再向Server端發起一個新連接,這時不能保證新連接的與剛關閉的連接的端口號是不同的,也就是新連接和老連接的端口號可能一樣了,那么就可能出現問題:如果前一次的連接某些數據滯留在網絡中,這些延遲數據在建立新連接后到達Client端,由於新老連接的端口號和IP都一樣,TCP協議就認為延遲數據是屬於新連接的,新連接就會接收到臟數據,這樣就會導致數據包混亂。所以TCP連接需要在TIME_WAIT狀態等待2倍MSL,才能保證本次連接的所有數據在網絡中消失。
確認應答機制

根據前面的知識,就是ack+1 的應答,當發數據端發1000數據,接收端就會告訴對方我需要發1001數據,下一次就從1001繼續發送。
滑動窗口
如果是一發一收的性質的話,效率太差,tcp提供滑動窗口就是實現快速發送。


就是開辟了緩沖區,來確認那些數據沒有發送,哪些數據可以從緩沖區刪除了。
如果在這種情況中出現了丟包現象,應該如何重發呢?
數據到達接收方,但是應答報文丟失:可以更具后邊的ACK確認。假設發送方發送1-1000的數據,接收方收到返回確認ACK,但是返回的ACK丟失了,另一邊發送1001-2000收到的確認ACK 2001,就可以認為1-1000數據接收成功 。
數據包之間丟失: 當某一段報文段丟失之后,發送端會一直收到 1001 這樣的ACK,就像是在提醒發送端 "我想要的是 1001" 一樣,如果發送端主機連續三次收到了同樣一個"1001" 這樣的應答,就會將對應的數據 1001 - 2000 重新發送,這個時候接收端收到了 1001 之后, 再次返回的ACK就是7001了。因為2001 - 7000接收端其實之前就已經收到了,被放到了接收端操作系統內核的接收緩沖區中。這種機制被稱為 “高速重發控制”(也叫 "快重傳")。
快恢復(與快重傳配合使用)
采用快恢復算法時,慢開始只在TCP連接建立時和網絡出現超時時才使用。
當發送方連續收到三個重復確認時,就執行“乘法減小”算法,把ssthresh門限減半。但是接下去並不執行慢開始算法。
考慮到如果網絡出現擁塞的話就不會收到好幾個重復的確認,所以發送方現在認為網絡可能沒有出現擁塞。所以此時不執行慢開始算法,而是將cwnd設置為ssthresh的大小,然后執行擁塞避免算法。

超時重傳機制
發送端沒有收到應答的數據,就會重新發送,有兩種情況

1、接收端就沒有收到數據,這時候發送端看時間到了,就會重新發送數據給接收端

2、當接收端收到數據,在應答包發送的時候出現了丟包,這個時候發送端沒有收到應答包還是會重新發送,但是接受端已經有數據了,這是就根據序列號來識別是否是重復的數據。
如何界定這個特定的時間重傳
最理想的情況下,找到一個最小的時間保證確認應答一定能在這時間內返回
但是這個時間的長短,隨着網絡環境的不同,也是有差異的。
如果超時時間設得太長,會影響整體的重傳效率
如果超時時間設的太短,有可能平凡的發送沖符的數據包。
TCP為了抱枕個無論在何種環境下都能比較高性能的通信,因此會動態計算這個最大超時時間:
Linux中,超時以500ms為一個單位進行控制,每次判定超時重發的超時時間都是500ms的整數倍。
如果重發一次,任然得不到應答,就等待2500ms后在進行重傳
如果還得不到應答,等待4500ms再重傳,一次類推,以指數形式遞增。
累積到一定的重傳次數,TCP認為網絡或者對端主機出現異常,就會強制關閉連接。
流量控制
當發送端發送速度過於快速,接收端緩沖區滿的時候,這是在發送數據就會出現丟包的現象,tcp會自動根據接收端的窗口大小,進行數據的大小的調整,這時候就要用到tcp報頭中的窗口的16字節的大小了,窗口數字越大,就證明接收端數據吞吐量越大。如果接收緩沖區滿了,就會將窗口置為0,這時發送方不再發送數據。但是需要定期的發送一個試探窗口,目的是為了取探測數據段,是接收端把窗口大小告訴發送端。

擁塞控制
如果網路中的網絡狀態不好,這是服務器發送大量的數據就會使網路跟家的阻塞,這是tcp就引入了慢啟動機制。先發少量的數據,后面在逐漸增加。

這種增加數據的吞吐量是一直指數形式增加的,但是不會這樣一直增加,tcp有個叫慢啟動閾值,當到這個數值時候,增長的就會以線性增長

- 當
TCP開始啟動的時候,慢啟動閾值等於窗口最大值 - 在每次
超時重發的時候,慢啟動閾值會變成原來的一半同時擁塞窗口置回1
延遲應答
就是加入這次緩沖區收到500k數據,立即返回應答包,返回的窗口大小也是500,但是如果等他個十幾毫秒,就會收到1M數據,就會返回窗口大小更大,服務端就會發送很多數據過來,吞吐量就很大。效率更高。
- 數量限制: 每隔
N個包就應答一次 - 時間限制: 超過大
延遲時間就應答一次
注:具體的數量和超時時間, 依操作系統不同也有差異; 一般N取2, 超時時間取200ms
捎帶應答
就是在延遲應答基礎上,看下你的網絡狀況是不是很好,接收端就會在ACK中捎帶應答,自己的網絡狀況。
如何避免粘包問題呢?明確兩個包之間的邊界
對於定長的包,保證每次都按固定大小讀取即可。例如一個Request結構, 是固定大小的, 那么就從緩沖區從頭開始按sizeof(Request)依次讀取即可
對於變長的包,可以在包頭的位置,約定一個包總長度的字段,從而就知道了包的結束位置。
對於變長的包,還可以在包和包之間使用明確的分隔符(應用層協議是程序員自己來定義的, 只要保證分隔符不和正文沖突即可)。
對於UDP協議,如果還沒有上層交付數據, UDP的報文長度仍然在。 同時UDP是一個一個把數據交付給應用層,這樣就有存在明確的數據邊界,站在應用層的角度, 使用UDP的時候要么收到完整的UDP報文要么不收,不會出現"半個"的情況。
TCP連接異常情況:
進程終止:進程終止會釋放文件描述符,仍然可以發送FIN,和正常關閉沒有什么區別。機器重啟和進程終止一樣。
機器掉電/網線斷開:接收端認為連接還在,一旦接收端有寫入操作,接收端發現連接已經不在了,就會進行reset。即使沒有寫入操作,TCP自己也內置了一個保活定時器,會定期詢問對方是否還在。如果對方不在,也會把連接釋放。應用層的某些協議, 也有一些這樣的檢測機制.例如HTTP長連接中, 也會定期檢測對方的狀態.Q在QQ 斷線之后, 也會定期嘗試重新連接。
參考:
https://blog.csdn.net/hansionz/article/details/86435127
https://blog.csdn.net/qq_40927789/article/details/80607610
