TCP中的連接和斷開可以說是在面試中經常被問到的問題之一,正好有空就總結一下,首先回顧一下TCP的相關知識點
1. TCP的基礎知識
1.1 TCP的基本概念
我們知道TCP是運輸層的面向連接的可靠的傳輸協議。面向連接的,指的就是在兩個進程發送數據之前,必須先相互“握手”,確保兩進程可以進行連接。並且這個傳輸是點對點的,即一個TCP連接中只有一個發送方和接收方;可靠的,指的是在任何網絡情況下,在TCP傳輸中數據都將完整的發送到接收方。
1.2 TCP的報文段結構
-
源端口和目的端口:和UDP一樣用於多路復用/分解來自或送到上一層
-
序號:一個報文段的序號是整個傳送的字節流序列,而不是該報文段的序列
-
確認號:主機正在等待的數據的下一個字節序號
-
數據偏移:指TCP首部的長度,可變。默認長度為20字節
-
窗口:用於流量控制,用於指示接收方願意接受的字節數量
-
標志字段:
- ACK:當該位為1時,確認號有效
- RST:該位為1時,表示TCP連接中出現異常必須強制斷開連接
- SYC:該位為1時,開始建立連接,並且序號字段進行序列號初始值的設定
- FIN:該位為1時,斷開連接,通信雙方相互交換FIN位置為1的TCP段后斷開連接
2. TCP連接
2.1 什么是連接(connection)和會話(Session)
連接是數據傳輸雙方的契約,在設計上,連接是一種傳輸數據的行為,具體來說,數據收發雙方的內存中都建立一個用於維護數據傳輸狀態的對象,比如TCP 的連接組成包括一台主機上的緩存、變量和與進程連接的套接字,以及另外一台主機上的緩存、變量和與進程連接的套接字。(由端口號和IP地址組成)所以連接是網絡行為狀態的記錄
而會話是應用的行為,比如說你在微信上給人發消息,打開應用聊天窗口和對方聊天是一個會話,但是連接只有在進行發消息、語音的時候連接才開啟。其他不發消息和語音時,連接可能暫時斷開,但是只要不關聊天窗口,會話時一直存在的。
總結而言,會話是應用層的概念,連接是傳輸層的概念,正是因為如此,在 TCP 連接的時候需要握手建立連接。
3. TCP連接建立
3.1 TCP 協議中的基本操作
也就是報文段的標志字段的含義和功能:
- SYN(Synchronization):請求同步,一個 Host 主動向另外一個 Host 發起連接。當 SYN=1,ACK=0 時,表示這是一個請求建立連接的報文段;當 SYN=1,ACK=1 時,表示對方同意建立連接
- PSH(Push): 數據推送,一個 Host 主動向另外一個 Host 發送數據
- FIN(Finish): 請求完成,一個 Host 主動斷開請求,如果 FIN=1,表示數據已經發送完成,可以釋放連接。
- ACK:表示前面的確認號字段是否有效。ACK=1 時表示有效。只有當 ACK=1 時,前面的確認號字段才有效。TCP 規定,連接建立后,ACK 必須為 1
- RST:表示是否重置連接。如果 RST=1,說明 TCP 連接出現了嚴重錯誤(如主機崩潰),必須釋放連接,然后再重新建立連接。
如圖,開始時,兩個端口都是出於closed狀態,當服務器端口變成listen時,監聽端口,是否有數據傳來。
-
第一步:客戶端向服務端發送一個特殊的TCP報文段。客戶端進入SYN_SENT狀態這個報文段有以下特點:
- 不包含應用層數據,封裝在一個IP數據報中發送給服務器
- SYN為1(此步是ACK唯一可為0處,其他時間均為1)
- 序號段有一個隨機生成的初始序號(client_isn)
-
第二步:服務器端收到上步客戶端的報文段后,同時為該TCP連接分配TCP緩存和變量,並向該客戶發送允許連接的報文段。服務器進入SYN_RCVD狀態,這個報文段特點有:
- 不包含應用層數據
- SYN為1,ACK為1
- 確認號段被置為client_isn + 1,序號段被置為server_isn
-
第三步:客戶端收到上步服務端的報文段后,客戶端為該連接分配緩存和變量,同時客戶端向服務器端發送報文段,這個報文端特點有:
- 可以包含應用層數據
- SYN為0,ACK為1
- 確認號段被置為server_isn + 1
兩端進入ESTABLISHED狀態,連接建立
4. TCP連接斷開
若客戶端決定要關閉該連接(服務器端也可以發起關閉)
- 第一次:客戶端發送帶有FIN被置為1的報文段,進入FIN_WAIT_1狀態,並等待一個來自服務器的帶有確認的TCP報文段。
- 第二次:服務器端收到該報文段后,向客戶端發送一個確認ACK報文段,進入CLOSE_WAIT狀態。
- 第三次:服務器端處理完數據后向客戶端發送FIN被置為1的報文段,進入LAST_ACK狀態。
- 第四次:客戶端收到服務器端的FIN報文段后,向服務器端發送一個確認ACK報文段,進入TIME_WAIT狀態,服務器接收到該ACK報文段后關閉,客戶端在經過2MSL(與具體實現有關,典型值是20s、1分鍾或2分鍾)等待后關閉。
5. 關於TCP連接的面試題
5.1 如何唯一確定一個TCP連接
可以通過四個變量來確定唯一的TCP連接:源地址、源端口、目標地址、目標端口來唯一確定一個TCP連接。其中源地址和目標地址的字段在IP頭部,作用是通過IP協議發送報文給哪個主機;源端口和目標端口是在TCP首部,作用是通過TCP協議發送主機中的哪個進程。
5.2 UDP和TCP有什么區別
兩者的區別
- UDP面向無連接,利用IP提供無連接的傳輸數據服務
- UDP可以支持一對多、一對一、多對多的交互通信
- UDP不保證可靠交付數據,傳輸過程中可能會丟包
- UDP首部只有固定的8字節;TCP首部最短20字節,能夠變化
應用場景
- UDP用於包總量較少的通信,如DNS、SNMP;還有視頻、音頻等多媒體通信,以及廣播通信等等
- TCP用於需要保證可靠性數據交付的場景,比如FTP、HTTP
5.3 為什么是三次握手?
為什么TCP連接建立過程中不是兩次或者四次,三次就是最優解了嗎?首先來看看兩次握手建立連接會發生什么。
兩次握手
如果連接過程是兩次握手來建立,在理想的網絡環境下是可以完成通信建立的,但是現實的網絡環境很復雜,有時候會導致歷史的報文段比新的報文段先到達服務器端,這時,如果沒有第三次握手,就會造成無法同步序列號情況的發生。舉個例子,客戶端發送新SYN報文段的序號是100,網絡環境中有舊的SYN報文端的序號是80,然而現在舊的先到達服務器端,那么服務器端則會返回一個確認號為81的SYN+ACK報文段,這個時候客戶端接收到的報文段和預期報文段會不一致,就會造成無法同步序列號,達不到TCP可靠運輸的效果,也會浪費資源。那么如果有第三次握手,這時客戶端會反饋一個RST報文段,終止這次連接,等待新的SYN到來,這樣保證數據的可靠性傳輸。
四次握手
四次握手可以對比四次揮手,客戶端和服務器端都要分別發送SYN和ACK報文段,來表示之前的SYN報文已經被成功接收。
然而四次握手可以簡化成三次,第二、三次可以優化成一次。所以三次是保證可靠性傳輸連接的最優解。
5.4 什么是SYN 泛洪?如何避免
SYN泛洪攻擊通過發送大量的TCP SYN報文段,而不完成第三次握手的步驟。因為大量的SYN報文段的發送,服務器不斷為這些半開連接分配資源,導致服務器的連接資源被消耗殆盡。
如何避免,現在有一種有效的防御系統,稱為SYN cookie,它是這樣工作的:
- 當服務器接收到一個SYN報文段時,它並不知道該報文段是來自一個合法用戶還是SYN泛洪攻擊的一部分。因此服務器不會為該報文段生成一個半開連接。相反,服務器會生成一個初始TCP序列號cookie值(由目的IP地址與端口號以及僅有該服務器知道的秘密數的一個復雜函數),並發送給客戶端
- 如果客戶是合法的,將會返回一個ACK報文段。而且當服務器收到該ACK后,需要驗證該ACK是與前面發送的SYN相對應,並生成一個具有套接字的全開的連接。如果沒有返回一個ACK報文段,則初始的SYN並沒有對服務器產生危害,因為服務器也沒為它分配任何資源。
5.5 為什么是四次揮手
四次揮手中雙方發送了FIN報文段,所以在客戶端發送FIN后,服務器端接收到后首先會回一個ACK應答報文,因為此時服務器端可能還有數據沒發送完,所以在服務端數據處理完后,才發送FIN報文段給客戶端表示現在可以關閉連接。正是因為這個等待過程,使得比三次握手多一次。
5.6 如果已經建立了連接,客戶端出現故障了怎么辦?
TCP有一個機制是保活機制:定義在一個時間段內,如果沒有任何連接相關的活動,TCP保活機制則開始作用,每隔一個時間間隔會發送一個探測報文,該探測報文包含的數據很少,如果連續幾個探測報文都沒有得到響應,說明該TCP連接已經死亡。
客戶端的故障也分為這幾種:
- 對端系統正常回復探測報文,TCP保活時間重置,等待下一個保活時間到來,TCP連接正常運行。
- 對端程序崩潰並重啟,此時可以對探測報完進行響應,但是沒有連接的有效消息,序列不符合,最后會產生RST報文,這時連接被重置。
- 對端程序徹底崩潰,無法響應探測報,經過幾次連續無響應后TCP會報告此連接已經死亡
5.7 為什么需要TIME_WAIT狀態
首先要說明,只有主動發起關閉連接的一方才會有TIME_WAIT狀態,那么為什么會有TIME_WAIT狀態,這時因為在服務端關閉后,可能還會有其他的數據報未到達客戶端,所以需要再等待一段時間。一般這個時間是2MSL時間,也就是報文段在兩端傳輸的最大往返時間。
TIME_WAIT狀態太多也會導致占用過多的端口資源,會導致無法創建新的連接