一、傳輸控制協議TCP簡介
1.1 簡介
TCP(Transmission Control Protocol) 傳輸控制協議,是一種 面向連線的、可靠的、基於位元組流的傳輸層 通訊協議。
TCP是一種面向連線(連線導向)的、可靠的基於位元組流的傳輸層通訊協議。TCP將使用者資料打包成報文段,它傳送後啟動一個定時器,另一端收到的資料進行確認、對失序的資料重新排序、丟棄重複資料。
TCP把連線作為最基本的物件,每一條TCP連線都有兩個端點,這種端點我們叫作套接字(socket),將埠號拼接到IP地址即構成了套接字,例如 192.1.1.6:50030
1.2 特點
- 面向連線的、可靠的、基於位元組流的 傳輸層 通訊協議
- 將應用層的資料流分割成文段併傳送給目標節點的TCP層
- 資料包都有序號,對方收到則傳送ACK確認,未收到則重傳
- 使用校驗和來檢驗資料在傳輸過程中是否有誤
二、TCP報文頭
1、源埠(Source Port)/ 目的埠(Destination Port):他們各佔2個位元組,標示該段報文來自哪裡(源埠)以及要傳給哪個上層協議或應用程式(目的埠)。進行tcp通訊時,一般client是通過系統自動選擇的臨時埠號,而伺服器一般是使用知名服務埠號或者自己指定的埠號
(比如DNS協議對應埠53,HTTP協議對應80)
2、序號(Sequence Number):佔據四個位元組,TCP是面向位元組流的,TCP連線中傳送的位元組流中的每個位元組都按順序編號,例如如一段報文的序號欄位值是
107
,而攜帶的資料共有
100個欄位
,如果有下一個報文過來,那麼序號就從
207(100+107)
開始,整個要傳送的位元組流的起始序號必須要在連線建立時設定。首部中的序號欄位值指的是本報文段所傳送的資料的第一個位元組的序號
3、確認序號(Acknowledgment Number):4個位元組,是期望收到對方下一個報文段的第一個資料位元組的序號,若確認號=N,則表明:到序號N-1為止的所有資料都已正確收到,例如:B收到A傳送過來的報文,其序列號欄位是
301
,而資料長度是
200位元組
,這表明了B正確的收到了A到序號
500(301+200-1)
為止的資料,因此B希望收到A的下一個資料序號是
501
,於是B在傳送給A的確認報文段中,會把ACK確認號設定為
501
4、資料偏移(Offset):4個位元組。指出TCP報文段的資料起始處距離報文段的起始處有多遠,這個欄位實際上是指出TCP報文段的首部長度。由於首部中還有長度不確定的選項欄位,因此資料偏移欄位是必要的。單位是32位字,也就是4位元組,4位二進位制最大表示15,所以資料偏移也就是TCP首部最大60位元組
5、保留(Reserved):6個位元組。保留域
6、TCP Flags:控制位,由八個標誌位組成,每個標誌位表示控制的功能,我們主要來介紹TCP Flags中常用的六個,
-
URG(緊急指標標誌):當
URG=1
時,表明緊急指標欄位有效。它告訴系統此報文段中有緊急資料,應儘快傳送(相當於高優先順序的資料),而不要按原來的排隊順序來傳送。例如,已經傳送了很長的一個程式在主機上執行。但後來發現了一些問題,需要取消該程式的執行。因此使用者從鍵盤發出中斷命令。如果不使用緊急資料,那麼這兩個字元將儲存在接收TCP的快取末尾。只有在所有的資料被處理完畢後這兩個字元才被交付接收方的應用程式。這樣做就浪費了許多時間 -
ACK(確認序號標誌):當
ACK=1
時確認號欄位有效。當ACK=0
時,確認號無效。TCP規定,在連線建立後所有的傳送的報文段都必須把ACK置1 -
PSH(push標誌):當兩個應用程式進行互動式的通訊時,有時在一端的應用程式希望在鍵入一個命令後立即就能收到對方的響應。在這種情況下,TCP就可以使用推送操作。這時,傳送方TCP把PSH置1,並立即建立一個報文段傳送出去。接收方TCP收到PSH=1的報文段,就儘快地交付接收應用程式,而不再等到整個快取都填滿了後向上交付
-
RST(重置連線標誌):TCP連線中出現嚴重差錯(如由於主機崩潰或其他原因),必須釋放連線,然後再重新建立運輸連線,可以用來拒絕一個非法的報文段或拒絕開啟一個連線
-
SYN(同步序號,用於建立連線過程):在連線建立時用來同步序號。當
SYN=1而ACK=0
時,表明這是一個連線請求報文段。對方若同意建立連線,則應在相應的報文段中使用SYN=1和ACK=1
。因此,SYN置為1就表示這是一個連線請求或連線接受保溫。 -
FIN(finish標誌,用於釋放連線):當
FIN=1
時,表明此報文段的傳送方的資料已傳送完畢,並要求釋放運輸連線
7、視窗(Window):
是TCP流量控制的一個手段
。這裡說的視窗,指的是接收通告視窗(Receiver Window,RWND)。它告訴對方本端的TCP接收緩衝區還能容納多少位元組的資料,這樣就可以控制傳送資料的速度
8、檢驗和(Checksum):檢驗範圍包括首部和資料兩部分,由傳送端填充,接收端對TCP報文段執行CRC演算法以檢驗TCP報文段在傳輸過程中是否損壞。這也是TCP可靠傳輸的一個重要保障
9、緊急指標(Urgent Pointer):緊急指標僅在URG=1時才有意義,它指出本報文段中的緊急資料的位元組數(緊急資料結束後就是普通資料)。因此,緊急指標指出了緊急資料的末尾在報文段中的位置。當所有緊急資料都處理完時,TCP就告訴應用程式恢復到正常操作。值得注意的是,即使視窗為零時也可傳送緊急資料。
10、TCP可選項(TCP Options):長度可變,最長可達40位元組。當沒有使用“選項”時,TCP的首部長度是20位元組。
三、TCP的三次握手
所謂三次握手(Three-Way Handshake)即建立TCP連線,就是指建立一個TCP連線時,需要客戶端和服務端總共傳送3個包以確認連線的建立。在socket程式設計中,這一過程由客戶端執行connect來觸發,整個流程如下圖所示:
在TCP/IP協議中,TCP協議提供可靠的連線服務,採用三次握手建立一個連線。
第一次握手: 建立連線時,客戶端傳送 SYN包(syn=j)到伺服器,並進入 SYN_SEND狀態,等待伺服器確認,SYN:同步序列編號(Synchronize Sequence Numbers)。
第二次握手: 伺服器收到 SYN 包,必須確認客戶的 SYN(ack=j+1),同時自己也傳送一個 SYN包(syn=k),即SYN+ACK包,此時伺服器進入 SYN_RECV狀態;
第三次握手: 客戶端收到伺服器的SYN + ACK包,向伺服器傳送確認包ACK(ack=k+1),此包傳送完畢,客戶端和伺服器進入ESTABLISHED(TCP連線成功)狀態,完成三次握手。
3.1 為什麼需要三次握手才能建立連線
- 為了初始化Sequence Number 的初始值,實現可靠資料傳輸, TCP 協議的通訊雙方, 都必須維護一個序列號, 以標識傳送出去的資料包中, 哪些是已經被對方收到的。 三次握手的過程即是通訊雙方相互告知序列號起始值, 並確認對方已經收到了序列號起始值的必經步驟
- 如果只是兩次握手, 至多隻有連線發起方的起始序列號能被確認, 另一方選擇的序列號則得不到確認
3.2 首次握手的隱患——SYN超時
一、問題起因分析:
- 伺服器收到客戶端的SYN,回覆SYN和ACK的時候未收到ACK確認
- 伺服器不斷重試直至超時,Linux預設等待63秒才斷開連線;(重複5次【不包括第一次】,從1秒開始,每次重試都翻倍:1+2+4+8+16+32=63秒)
二、針對SYN Flood的防護措施:
- SYN佇列滿後,通過tcp_syncookies引數會發SYN cookie【源埠+目標埠+時間戳組成】
- 若為正常連線則Client會回發SYN Cookie,直接建立連線;
3.3 保活機制:
當我們建立連線後,Client出現故障怎麼辦?
- 向對方傳送保活探測報文,如果未收到相應則繼續傳送;
- 嘗試次數達到保活探測數仍未收到相應則中斷連線;
四、TCP的四次揮手
所謂四次揮手(Four-Way Wavehand)即終止TCP連線,就是指斷開一個TCP連線時,需要客戶端和服務端總共傳送4個包以確認連線的斷開。在socket程式設計中,這一過程由客戶端或服務端任一方執行close來觸發,整個流程如下圖所示:
由於TCP連線時全雙工的,因此,每個方向都必須要單獨進行關閉,這一原則是當一方完成資料傳送任務後,傳送一個FIN來終止這一方向的連線,收到一個FIN只是意味著這一方向上沒有資料流動了,即不會再收到資料了,但是在這個TCP連線上仍然能夠傳送資料,直到這一方向也傳送了FIN。首先進行關閉的一方將執行主動關閉,而另一方則執行被動關閉。
- 第一次揮手: Client傳送一個FIN,用來關閉Client到Server的資料傳送,Client進入FIN_WAIT_1狀態
- 第二次揮手: Server收到FIN後,傳送一個ACK給Client,確認序號為收到序號+1(與SYN相同,一個FIN佔用一個序號),Server進入CLOSE_WAIT狀態
- 第三次揮手: Server傳送一個FIN,用來關閉Server到Client的資料傳送,Server進入LAST_ACK狀態
- 第四次揮手: Client收到FIN後,Client進入 TIME_WAIT狀態,接著傳送一個ACK給Server,確認序號為收到序號+1,Server進入CLOSED狀態,完成四次揮手
一、為什麼會有TIME_WAIT狀態
客戶端連線在收到伺服器的結束報文段之後,不會直接進入CLOSED狀態,而是轉移到TIME_WAIT狀態。在這個狀態,客戶端連線要等待一段長為2MSL,即兩倍的報文段最大生存時間,才能完全關閉,其原因主要有兩點:
- 確保有足夠的時間放對方收到ACK包
- 避免新舊連線混淆
二、為什麼需要四次握手才能斷開連線
因為TCP連線是全雙工的網路協議,允許同時通訊的雙方同時進行資料的收發,同樣也允許收發兩個方向的連線被獨立關閉,以避免client資料傳送完畢,向server傳送FIN關閉連線,而server還有傳送到client的資料沒有傳送完畢的情況。所以關閉TCP連線需要進行四次握手,每次關閉一個方向上的連線需要FIN和ACK兩次握手,傳送發和接收方都需要FIN報文和ACK報文
三、伺服器出現大量CLOSE_WAIT狀態的原因
是由於對方關閉socket連線,我方忙於讀或寫,沒有及時關閉連線
當客戶端因為某種原因先於服務端發出了FIN訊號,就會導致服務端被動關閉,若服務端不主動關閉socket發FIN給Client,此時服務端Socket會處於CLOSE_WAIT狀態(而不是LAST_ACK狀態)。通常來說,一個CLOSE_WAIT會維持至少2個小時的時間(系統預設超時時間的是7200秒,也就是2小時)。如果服務端程式因某個原因導致系統造成一堆CLOSE_WAIT消耗資源,那麼通常是等不到釋放那一刻,系統就已崩潰
解決:
1、檢查程式碼,特別是釋放資源的程式碼
2、檢查配置,特別是處理請求的執行緒配置
Linux的檢查程式碼:
netstat -n | awk '/^tcp/{++S[$NF]}END{for(a in S) print a,S[a]}'
五、總結
到這裡TCP的三次握手四次揮手就講完了,好久都沒有寫技術文章了,寫了一下,感覺還挺好的,上面是博主的認識,有寫的不好的地方,大家可以在評論區討論或者提問,博主看到了會第一時間回覆大家,最近也準備開始面試了,先好好準備一下,希望今年可以找到心滿意足的工作,也希望今年面試的小夥伴們都有一個好的office,大家一起加油,我是牧小農,我喂自己帶鹽,大家加油。