首先要先了解TCP數據包的一些字段的作用: 這里只列出了用到的幾個重要的。
- 序號(seq):占4個字節,32位。 發送端告訴接收端這組的首部第一個是第多少個字節。
一整個數據包要放到緩沖區中分組傳輸,分組之后的每個數據包都要加上TCP首部,這里表明發送方發送該組的第一個字節是一整個大數據包(沒有放緩沖區的分組之前的整個數據包)的第多少個字節。
- 確認號(ack):占4個字節,32位。接收端告訴發送端我已經收到了多少字節,該發下一個字節了(該位為,收到的字節數+1)。
- ACK:占1位。如果該位為0,表示確認號無效,如果是1表示確認號有效。
- SYN:占1位。請求建立會話連接。當SYN=1時表示同意建立會話。
SYN=1,ACK=0 :表示請求連接的的數據包。其實ACK是沒有。
SYN=1,ACK=1 :表示響應同意建立連接的數據包。
1.三次握手:
就是在進行可靠傳輸發送數據之前,要連續三次打招呼建立連接的過程。
- 三次握手的過程:

第一次握手:在每次發送之前客戶端都會向服務端發送SYN(seq=x)數據包請求建立連接,客戶端進入SYN_SENT狀態。
第二次握手:服務器收到SYN包,必須確認客戶的SYN(ack=x+1),同時自己也發送一個SYN包(seq=y),即SYN+ACK包,此時服務器進入SYN_RECV狀態
第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=y+1),此包發送完畢,客戶端和服務器進入ESTABLISHED(TCP連接成功)狀態,完成三次握手。
簡單理解三次握手:
TCP的三次握手還是挺有意思的,基本思想就是“讓我知道你已經知道”了。
服務器監聽請求,客戶端發起連接請求(第一次握手:客戶端->服務端:客戶端請求建立連接)。請求在路上可能存在丟失的風險, 所以當請求到了服務器后如果服務器同意建立連接會給客戶端一個回信(第二次握手:服務端->客戶端:服務端同意建立連接),告訴它:我已經收到請求,可以連接。 但是回信也存在一個問題,那就是回信能不能到客戶端?它需要客戶端給他一個回信說我已經收到批准通知了, 如果客戶端一直不回復的話意味着客戶端沒有收到批准通知。 因此客戶端一收到批准通知就立馬回復(第三次握手:客戶端->服務端:客戶端收到了服務端的同意連接的數據包):OK老鐵我收到你的批准通知了。
三次握手結束,服務端開始進行可靠傳輸發送數據。
- 三次握手的作用:
為了防止服務器端開啟一些無用的連接增加服務器的開銷,以及防已經失效的連接請求突然又傳到了服務端,從而產生錯誤。
2.四次揮手:
就是連續四次告別。即終止TCP連接的過程。
- 四次揮手的過程:
由於TCP連接是全雙工的,所以兩端都可以先揮手,並且每個方向都必須單獨進行關閉。
原則是:當任意一方完成它的數據發送任務后,就能發送一個FIN的數據包來終止這個方向的連接。收到一個FIN只意味着這一方向上沒有數據流動,另一個TCP連接在收到一個FIN后仍能發送數據。首先進行關閉的一方將執行主動關閉,而另一方執行被動關閉。
![]() |
FIN_WAIT_2:上面已經詳細解釋了這種狀態,實際上FIN_WAIT_2狀態下的SOCKET,表示半連接,也即有一方要求close連接,但另外還告訴對方,我暫時還有點數據需要傳送給你,稍后再關閉連接。 |
TIME_WAIT: 表示收到了對方的FIN報文,並發送出了ACK報文,就等2MSL后即可回到CLOSED可用狀態了。如果FIN_WAIT_1狀態下,收到了對方同時帶 FIN標志和ACK標志的報文時,可以直接進入到TIME_WAIT狀態,而無須經過FIN_WAIT_2狀態。
|
|
CLOSING: 這種狀態比較特殊,實際情況中應該是很少見,屬於一種比較罕見的例外狀態。正常情況下,當你發送FIN報文后,按理來說是應該先收到(或同時收到)對方的 ACK報文,再收到對方的FIN報文。但是CLOSING狀態表示你發送FIN報文后,並沒有收到對方的ACK報文,反而卻也收到了對方的FIN報文。什 么情況下會出現此種情況呢?其實細想一下,也不難得出結論:那就是如果雙方幾乎在同時close一個SOCKET的話,那么就出現了雙方同時發送FIN報 文的情況,也即會出現CLOSING狀態,表示雙方都正在關閉SOCKET連接。 | |
FIN_WAIT_1: 其實FIN_WAIT_1和FIN_WAIT_2狀態的真正含義都是表示等待對方的FIN報文。而這兩種狀態的區別 是:FIN_WAIT_1狀態實際上是當SOCKET在ESTABLISHED狀態時,它想主動關閉連接,向對方發送了FIN報文,此時該SOCKET即 進入到FIN_WAIT_1狀態。而當對方回應ACK報文后,則進入到FIN_WAIT_2狀態,當然在實際的正常情況下,無論對方何種情況下,都應該馬 上回應ACK報文,所以FIN_WAIT_1狀態一般是比較難見到的,而FIN_WAIT_2狀態還有時常常可以用netstat看到。
|
CLOSE_WAIT: 這種狀態的含義其實是表示在等待關閉。怎么理解呢?當對方close一個SOCKET后發送FIN報文給自己,你系統毫無疑問地會回應一個ACK報文給對 方,此時則進入到CLOSE_WAIT狀態。接下來呢,實際上你真正需要考慮的事情是察看你是否還有數據發送給對方,如果沒有的話,那么你也就可以 close這個SOCKET,發送FIN報文給對方,也即關閉連接。所以你在CLOSE_WAIT狀態下,需要完成的事情是等待你去關閉連接。 |
LAST_ACK: 這個狀態還是比較容易好理解的,它是被動關閉一方在發送FIN報文后,最后等待對方的ACK報文。當收到ACK報文后,也即可以進入到CLOSED可用狀態了。 |
第一次揮手:客戶端發送一個FIN=M,來關閉客戶端到服務器端的數據傳送,客戶端進入FIN_WAIT_1狀態。 意思是說"我客戶端沒有數據要發給你了,准備要關閉連接了",但是如果你服務器端沒有准備好的話,則不必急着關閉連接,可以先准備准備。
第二次揮手:服務器端收到FIN后,向客戶端發送ack=M+1,回復確認收到了客戶端的FIN的數據包,但是服務端要准備一下。 意思是告訴客戶端,你的請求我收到了,但是我還沒准備好,請繼續你等我的消息。這個時候客戶端就進入FIN_WAIT_2狀態,客戶端繼續等待服務器端的FIN報文。
第三次揮手:當服務器端准備已經完成,准備關閉連接,則向客戶端發送FIN=N報文。告訴客戶端,我這邊准備好了,要關閉連接了。服務器端進入LAST_ACK狀態。
第四次揮手:客戶端收到FIN=N報文后,就知道可以關閉連接了,但是他還是不相信網絡,怕服務器端不知道要關閉,所以客戶端向服務器端發送ack=N+1后進入TIME_WAIT狀態,如果服務器端沒有收到ACK則可以重傳。服務器端收到ACK后,就知道可以斷開連接了。客戶端等待了2MSL(MSL:報文最大生存時間。RFC 793中規定了MSL為2分鍾,實際應用中常用的是30秒,1分鍾和2分鍾等)后依然沒有收到回復,則證明服務器端已正常關閉,那好,我客戶端也可以關閉連接了。最終完成了四次握手。
簡單理解四次揮手:
1、女的跟男的說:對不起,我不喜歡你了,分手吧。
2、男的跟女的說:嗯,但是我還沒准備好,讓我准備准備。
3、男的跟女的說:我准備好了,分手吧,再見。
4、女的跟男的說:好的,再見。
3.三次握手和四次揮手中的常見問題:
1.為什么連接的時候是三次握手,關閉的時候卻是四次握手?
答:因為當Server端收到Client端的SYN連接請求報文后,可以直接發送SYN+ACK報文。其中ACK報文是用來應答的,SYN報文是用來同步的。但是關閉連接時,當Server端收到FIN報文時,很可能並不會立即關閉SOCKET,所以只能先回復一個ACK報文,告訴Client端,"你發的FIN報文我收到了"。只有等到我Server端所有的報文都發送完了,我才能發送FIN報文,因此不能一起發送。故需要四步握手。
2.為什么TIME_WAIT狀態需要經過2MSL(最大報文段生存時間)才能返回到CLOSE狀態?
答:雖然按道理,四個報文都發送完畢,我們可以直接進入CLOSE狀態了,但是我們必須假象網絡是不可靠的,有可以最后一個ACK丟失。所以TIME_WAIT狀態就是用來重發可能丟失的ACK報文。在Client發送出最后的ACK回復,但該ACK可能丟失。Server如果沒有收到ACK,將不斷重復發送FIN片段。所以Client不能立即關閉,它必須確認Server接收到了該ACK。Client會在發送出ACK之后進入到TIME_WAIT狀態。Client會設置一個計時器,等待2MSL的時間。如果在該時間內再次收到FIN,那么Client會重發ACK並再次等待2MSL。所謂的2MSL是兩倍的MSL(Maximum Segment Lifetime)。MSL指一個片段在網絡中最大的存活時間,2MSL就是一個發送和一個回復所需的最大時間。如果直到2MSL,Client都沒有再次收到FIN,那么Client推斷ACK已經被成功接收,則結束TCP連接。
3.為什么不能用兩次握手進行連接?
答:3次握手完成兩個重要的功能,既要雙方做好發送數據的准備工作(雙方都知道彼此已准備好),也要允許雙方就初始序列號進行協商,這個序列號在握手過程中被發送和確認。現在把三次握手改成僅需要兩次握手,死鎖是可能發生的。作為例子,考慮計算機S和C之間的通信,假定C給S發送一個連接請求分組,S收到了這個分組,並發 送了確認應答分組。按照兩次握手的協定,S認為連接已經成功地建立了,可以開始發送數據分組。可是,C在S的應答分組在傳輸中被丟失的情況下,將不知道S 是否已准備好,不知道S建立什么樣的序列號,C甚至懷疑S是否收到自己的連接請求分組。在這種情況下,C認為連接還未建立成功,將忽略S發來的任何數據分 組,只等待連接確認應答分組。而S在發出的分組超時后,重復發送同樣的分組。這樣就形成了死鎖。
4.如果已經建立了連接,但是客戶端突然出現故障了怎么辦?
答:TCP還設有一個保活計時器,顯然,客戶端如果出現故障,服務器不能一直等下去,白白浪費資源。服務器每收到一次客戶端的請求后都會重新復位這個計時器,時間通常是設置為2小時,若兩小時還沒有收到客戶端的任何數據,服務器就會發送一個探測報文段,以后每隔75秒鍾發送一次。若一連發送10個探測報文仍然沒反應,服務器就認為客戶端出了故障,接着就關閉連接。