按照國際慣例,了解或者學(xué)習(xí)一個東西之前,先過一遍這個東西的定義。
TCP:
傳輸控制協(xié)議(英語:Transmission Control Protocol,縮寫為TCP)是一種面向連接的、可靠的、基于字節(jié)流的傳輸層通信協(xié)議,由IETF的RFC 793定義。( 維基百科 )
TCP
TCP 相關(guān)知識點是我們程序員來說必須掌握的一個基本技能點?;旧鲜敲嬖嚤貑栴}( 當(dāng)然大佬就不需要問了咯 )。
TCP 是在傳輸層上工作的一個比較復(fù)雜協(xié)議。比起 UDP 來說,TCP 的復(fù)雜程度簡直不是一個量級的關(guān)系。 了解傳輸層 —— 走你
TCP 特性
1.面向連接可靠的傳輸協(xié)議。
2.不支持組播地址,僅支持單播地址。
3.傳輸基于字節(jié)流,不是報文。
4.通過滑動窗口機(jī)制實現(xiàn)流量控制,根據(jù)網(wǎng)絡(luò)狀態(tài)動態(tài)改變窗口的大小進(jìn)行擁塞控制。
5.支持對數(shù)據(jù)進(jìn)行分節(jié)排序,保證數(shù)據(jù)完整型以及正確性。
6.通過序號的確認(rèn)和重發(fā),確保傳輸?shù)目煽啃浴?/p>
TCP 包頭

源端口號: 這個字段占了 UDP 包頭的前面的 16 bits( 兩個字節(jié) ). 一般是包含發(fā)送數(shù)據(jù)包的應(yīng)用程序使用的端口。偶爾我們想實現(xiàn)單向傳輸?shù)臅r候,就可以在源端口填 0,這樣接收端就不知道,發(fā)送端的端口地址。
目的端口: 16 bits表示接收端的應(yīng)用程序的端口地址。
序列號:主要是在三次握手的時候,會同步這個數(shù)據(jù),以保證數(shù)據(jù)的連續(xù)的正確,不會出現(xiàn)分節(jié)數(shù)據(jù)的亂序問題
當(dāng) SYN 標(biāo)記不為 1 時,這是當(dāng)前數(shù)據(jù)分段第一個字節(jié)的序列號,如果 SYN 的值是 1 ,這個字段的值就是初始序列值( ISN ), 用于對序列號進(jìn)行同步,這時第一個字節(jié)的序列號比這個字段的值大 1 (也就是 ISN 加 1)。
確認(rèn)號:主要用于數(shù)據(jù)確認(rèn)的標(biāo)識,保證數(shù)據(jù)的可靠性。如果丟包,通過確認(rèn)號重發(fā)丟失的分節(jié)數(shù)據(jù)。這個數(shù)據(jù)只有在 ACK 為 1 時才有意義。
用于確認(rèn)已經(jīng)接收到的數(shù)據(jù)分節(jié),具體的數(shù)值是即將接收的下一個序列號,也就是已接收到的分節(jié)序列號加 1 。
數(shù)據(jù)偏移:表示的是包頭的長度,接收到可以知道那個地方數(shù)據(jù)就是真正的數(shù)據(jù)區(qū)。單位是 32 bits( 4個字節(jié) )。因為這個長度是 4 bits。所以最大是 15 。因此 TCP 的包頭最大是 60 個字節(jié)(包含端口號)。目前大多數(shù)是 20 字節(jié)。
保留:暫時未有使用,必須為 0,
URG:這個值為 1 時,標(biāo)識緊急指針區(qū)域有效,督促中間層設(shè)備要盡快處理這些數(shù)據(jù)。
ACK:這個值為 1 時,標(biāo)識確認(rèn)號有意義。
PSH:當(dāng)這個值為 1 時,表示 Push 操作,Push 操作就是數(shù)據(jù)到達(dá)接收到后,立即傳送給應(yīng)用程序,而不需要在緩沖區(qū)排隊(正常情況下,需要等到緩存區(qū)塞滿之后上報給應(yīng)用程序)。
RST:當(dāng)這個值為 1 時,連接會被重置,用來針對那些錯誤或者非法連接。
SYN:當(dāng)這個值為 1 時,表示同步序列號,主要是在建立連接的時候,三次握手時同步序列號。
在三次握手的時候 SYN = 1 ACK = 0 表示請求連接, SYN = 1, ACK = 1 表示連接被響應(yīng)。
FIN:當(dāng)這個值為 1 時,表示發(fā)送端的數(shù)據(jù)發(fā)送完成,沒有其他的數(shù)據(jù)了。這是一個連接斷開的標(biāo)識。
窗口大?。?/strong>這是窗口滑動機(jī)制進(jìn)行流量控制和窗口大小進(jìn)行擁塞控制。這是一個很深的問題,后續(xù)的相關(guān)文章可以聊一聊。
檢驗和:這個檢驗和 UDP 一致,發(fā)端生成后,接收端通過二進(jìn)制計算對接收的數(shù)據(jù)進(jìn)行相關(guān)的校驗。確保數(shù)據(jù)的完整以及正確性
緊急指針:標(biāo)識數(shù)據(jù)中有多少是緊急數(shù)據(jù),緊急數(shù)據(jù)放在數(shù)據(jù)的開頭。
選項:額外的填充數(shù)據(jù),確保包頭數(shù)據(jù)是 32 bits 的倍數(shù)關(guān)系。
數(shù)據(jù):需要傳輸?shù)臄?shù)據(jù)。
理解數(shù)據(jù)協(xié)議是理解三次握手以及四次揮手、窗口的前提條件。
TCP 三次握手
TCP 是面向連接的,數(shù)據(jù)通信之前必須要建立連接,這個連接可以是 1 對多。
TCP 三次握手的過程

上圖展示三次握手的流程圖,文字描述過程如下:
客戶端和服務(wù)端從 CLOSED 的狀態(tài)變成在線狀態(tài)。服務(wù)端需要處于 LISTEN.
1、客戶端向服務(wù)端發(fā)起一個連接。報文中包含了:
SYN = 1,
「序列號」的值,
發(fā)送之后客服端進(jìn)入到 SYN-SENT 狀態(tài)。
2、服務(wù)端接收到 SYN 和 序列號信息后,需要對這個報文進(jìn)行的確認(rèn)。服務(wù)端收到客戶端發(fā)送過來的「序列號」的值,服務(wù)端會返回一個「確認(rèn)號」的值(序列號+1)。同時返回
SYN = 1 表示還在同步階段。
ACK = 1 表示確認(rèn)號有意義。
序列號:表示服務(wù)端的序列號 y。
確認(rèn)號:針對客戶端發(fā)起連接的一個確認(rèn)號。
服務(wù)端發(fā)送之后,進(jìn)入到 SYN-RCVD 狀態(tài)。
3、客戶端接收到服務(wù)端返回的數(shù)據(jù),使得客戶端這一邊的相關(guān)通信建立,并且同步了相關(guān)序列號和確認(rèn)號的數(shù)值,但是服務(wù)端還沒有接收到客戶端回復(fù)的確認(rèn)號。所以客戶端需要再發(fā)送一個數(shù)據(jù)到服務(wù)端,主要包含:
ACK = 1,這是確認(rèn)號的數(shù)據(jù)有意義。
SYN = 0 序列號已經(jīng)同步完成,不需要再同步序列號。
序列號:因為 SYN 已經(jīng)不是第一次同步序列號的信息了。這個時候的序列號,就表示的是一個單純的基于序列號最新的數(shù)據(jù)包的序列號。其值為:x+1。
確認(rèn)號:返回服務(wù)端序列號 y 的確認(rèn)號,為 y+1。
發(fā)送這個值之后,客戶端進(jìn)入到 ESTABLISHED 狀態(tài),服務(wù)端接受到這個值之后也進(jìn)入到ESTABLISHED 狀態(tài)。然后就可以開始傳遞數(shù)據(jù)通信了。
至此三次握手已經(jīng)完成。
TCP 為什么進(jìn)行是三次握手?
為什么 TCP 需要進(jìn)行三次握手,這個問題很多人的解釋都引用了
為了防止已失效的連接請求報文段突然又傳送到了服務(wù)端,因而產(chǎn)生錯誤?!?謝希仁的《計算機(jī)網(wǎng)絡(luò)》
首先我們需要明白三次握手的意義在于什么,也就是說三次握手需要達(dá)到的一個目的是什么,
簡單來說,其實是為了同步序列號和確認(rèn)號的相關(guān)信息,而序列號和確認(rèn)號又是保障數(shù)據(jù)的正確性以及窗口滑動機(jī)制的最基本的根據(jù)。所以我最需要明白的可能還是為什么要同步序列號就是為什么要三次握手:(舉個例子來說明)(
如果 A 給 B發(fā)送一個數(shù)據(jù)包,這個包由于網(wǎng)絡(luò)的原因,很久才到 B 端,而這段時間,A 和 B 已經(jīng)斷開,并且重新建立了連接關(guān)系。沒有相關(guān)同步序列號和確認(rèn)號,B 端認(rèn)為這個數(shù)據(jù)還是認(rèn)為 A 這個時候要發(fā)給我的,但是其實這個數(shù)據(jù)上一次傳輸?shù)臄?shù)據(jù),相當(dāng)于這次傳輸?shù)臄?shù)據(jù)中插入了其他的數(shù)據(jù),那么就會導(dǎo)致這次的數(shù)據(jù)出現(xiàn)異常。
對于每次連接的序列號基本都是不一樣的,這個序列號是隨著時間會變化的,這個是一個 4 個字節(jié)的計數(shù)器。每隔 4 ms就會自加 1。如果需要產(chǎn)生重復(fù)的,需要 4 個小時才會回到同一個序列號。4 個小時的時候,那么在網(wǎng)絡(luò)中的上一次數(shù)據(jù)包早已經(jīng)失效了。在 IP 協(xié)議中,每個 IP 包頭都是有 TIL ( 生存時間的)。
為什么不是兩次:
對于 A 來看說,兩次實現(xiàn)了信息一來一回,但是 TCP 的數(shù)據(jù)的可靠性,如果要實現(xiàn)對于 B 來說的信息的一來一回,那么第三次是一定要存在的。對兩端的數(shù)據(jù)一來一回才是建立可靠連接的基本要求。
為什么不是四次:
理論上其實這個同步序列號和確認(rèn)號的過程,大于三次也是沒有的,應(yīng)該說幾十次上百次都是可以的,但是三次握手的過程已經(jīng)實現(xiàn)了序號數(shù)據(jù)同步,在進(jìn)行太多次的序號同步,已經(jīng)沒有意義。無故浪費寬帶。
解析 TCP 四次揮手
當(dāng)通信完成,當(dāng)然需要能夠進(jìn)行連接斷開。
TCP 四層揮手的過程:

1、任意一段( A 端)發(fā)出一條 FIN 的數(shù)據(jù)。數(shù)據(jù)包含:
FIN = 1,
序列號
A 端發(fā)出這個數(shù)據(jù)之后,進(jìn)入到 FIN - WAIT -1 的狀態(tài),等待 B 端的回復(fù).
2、B 收到 A 端的 FIN 的信息就回復(fù)一個數(shù)據(jù),表示已經(jīng)知道 A 端請求斷開連接這個事情了。此時 B 端進(jìn)入到 CLOSED - WAIT 的狀態(tài)。A 端接收到這個數(shù)據(jù)進(jìn)入到 FIN - WAIT -2 的狀態(tài)。 數(shù)據(jù)中包含 :
ACK = 1
確認(rèn)號
3、B 端給 A 端發(fā)起一次 FIN 的數(shù)據(jù)。請求結(jié)束連接,發(fā)送完成之后 B 端進(jìn)入到 LAST - ACK 狀態(tài)。發(fā)送的數(shù)據(jù)包含:
FIN = 1;
ACK = 1;
序列號
確認(rèn)號
4、 A 端接收到這個 B 端發(fā)送的 FIN 數(shù)據(jù)進(jìn)入到 TIME- WAIT 狀態(tài),同時回復(fù) B 端,已經(jīng)接收到了 FIN 數(shù)據(jù)。回復(fù)的包中包含:
ACK = 1;
確認(rèn)號
至此整個過程全部結(jié)束。順利斷開連接。
其實上述是正常且十分順利的四次揮手。
其中會出現(xiàn)多次不同的異常。
異常1:如果在 FIN-WAIT - 1 A 端不能接收到 B 斷的 ACK 信息。TCP 協(xié)議層上是沒有對這個地方任何處理的,但是 LINUX 是有相關(guān)處理的,有一個 TIMEOUT 的參數(shù)。FIN-WAIT-1 這個過程中其實不容易出異常的。因為在 TCP 的協(xié)議中,每次發(fā)送一個數(shù)據(jù),另外一端會立馬回復(fù) ACK 確認(rèn)信息的。所以理論上來說 A 端一般很少會能夠直接看到 FIN - WAIT 1.
A端:
FIN-WAIT 1 —— A 端可以數(shù)據(jù)都已經(jīng)發(fā)送完成,但是 B 端可能還有數(shù)據(jù)處理完成。
FIN-WAIT 2 -- B 端回復(fù)了 A 端結(jié)束連接的請求,并且做出了返回之后, A 進(jìn)入一種半關(guān)閉的等待的狀態(tài)。
TIME -WAIT —— 協(xié)議規(guī)定 A 端必須有一個駐守時間。這是一個為了異常處理而規(guī)定的時間。
B端:
COLSED - WAIT -- 數(shù)據(jù)沒有處理,處理完成沒有處理完的數(shù)據(jù)。
LAST - ACK -- 這是 B 給 A 端發(fā)送一個 FIN 請求之后,進(jìn)入到一個等待 A 端回復(fù)的狀態(tài)。
異常2:在 B 端發(fā)送一個 FIN 之后, 由于種種原因 B 端沒有接收到 A 端的 ACK. 此時 根據(jù) TCP 協(xié)議自身的機(jī)制 B 端重新發(fā)送 FIN 請求數(shù)據(jù)。但是如果這個時候 A 端已經(jīng)回復(fù)了 B 的 ACK ,到了設(shè)置的 TIME - WAIT 的時間之后,就會關(guān)閉這個連接。如果后續(xù)還收到這個信息。那么直接會 RST ( 錯誤連接或者非法連接 )
為什么需要設(shè)置一個 TIME-WAIT 的時間:
這個文章講的特別好(https://blog.csdn.net/smileiam/article/details/78226816)
一、保證TCP協(xié)議的全雙工連接能夠可靠關(guān)閉
先說第一點,如果Client直接CLOSED了,那么由于IP協(xié)議的不可靠性或者是其它網(wǎng)絡(luò)原因,導(dǎo)致Server沒有收到Client最后回復(fù)的ACK。那么Server就會在超時之后繼續(xù)發(fā)送FIN,此時由于Client已經(jīng)CLOSED了,就找不到與重發(fā)的FIN對應(yīng)的連接,最后Server就會收到RST而不是ACK,Server就會以為是連接錯誤把問題報告給高層。這樣的情況雖然不會造成數(shù)據(jù)丟失,但是卻導(dǎo)致TCP協(xié)議不符合可靠連接的要求。所以,Client不是直接進(jìn)入CLOSED,而是要保持TIME_WAIT,當(dāng)再次收到FIN的時候,能夠保證對方收到ACK,最后正確的關(guān)閉連接。
二、保證這次連接的重復(fù)數(shù)據(jù)段從網(wǎng)絡(luò)中消失
再說第二點,如果Client直接CLOSED,然后又再向Server發(fā)起一個新連接,我們不能保證這個新連接與剛關(guān)閉的連接的端口號是不同的。也就是說有可能新連接和老連接的端口號是相同的。一般來說不會發(fā)生什么問題,但是還是有特殊情況出現(xiàn):假設(shè)新連接和已經(jīng)關(guān)閉的老連接端口號是一樣的,如果前一次連接的某些數(shù)據(jù)仍然滯留在網(wǎng)絡(luò)中,這些延遲數(shù)據(jù)在建立新連接之后才到達(dá)Server,由于新連接和老連接的端口號是一樣的,又因為TCP協(xié)議判斷不同連接的依據(jù)是socket pair,于是,TCP協(xié)議就認(rèn)為那個延遲的數(shù)據(jù)是屬于新連接的,這樣就和真正的新連接的數(shù)據(jù)包發(fā)生混淆了。所以TCP連接還要在TIME_WAIT狀態(tài)等待2倍MSL,這樣可以保證本次連接的所有數(shù)據(jù)都從網(wǎng)絡(luò)中消失。
TCP 為什么進(jìn)行是四次揮手?
斷開比連接更復(fù)雜,比較直接的理解是資源回收比資源分配會更麻煩。使得所有資源能夠有效并且不產(chǎn)生錯誤的情況下釋放。如上所述,四次揮手的意義。