簡介
- TCP理論
- TCP報文格式
- 三次握手
- 三次握手圖解
- 為什么要三次握手
- 四次分手
- 四次分手圖解
- TCP的半關(guān)閉
- 實戰(zhàn)抓包演示(Wireshark)
TCP理論
TCP提供了一種面向連接的、可靠的字節(jié)流服務(wù)。
面向連接:接雙方在通信前需要預(yù)先建立一條連接,這猶如實際生活中的打電話。
- 應(yīng)用數(shù)據(jù)分割成TCP認(rèn)為最適合發(fā)送的數(shù)據(jù)塊。
- 重傳機制。設(shè)置定時器,等待確認(rèn)包
- 對首部和數(shù)據(jù)進(jìn)行校驗
- TCP對收到的數(shù)據(jù)進(jìn)行排序,然后交給應(yīng)用層
- TCP的接收端丟棄重復(fù)的數(shù)據(jù)
- TCP還提供流量控制
TCP連接必須要經(jīng)歷三次握手,而釋放一個TCP連接需要四次握手,這是由TCP的半關(guān)閉特性造成的。因為TCP連接時全雙工的,因此,需要TCP兩端要單獨執(zhí)行關(guān)閉。值得注意的是,主動關(guān)閉的一端在發(fā)送FIN之后,依然還能正常接收對方的數(shù)據(jù),只是通知對方它已經(jīng)沒有數(shù)據(jù)需要發(fā)送了,同理,被動關(guān)閉的一端在收到FIN之后,仍然可以發(fā)送數(shù)據(jù),直到它自身同樣發(fā)出FIN之后,才停止發(fā)送數(shù)據(jù)。
TCP報文格式

- Source Port和Destination Port:分別占用16位,表示源端口號和目的端口號;用于區(qū)別主機中的不同進(jìn)程,而IP地址是用來區(qū)分不同的主機的,源端口號和目的端口號配合上IP首部中的源IP地址和目的IP地址就能唯一的確定一個TCP連接;
- Sequence Number:用來標(biāo)識從TCP發(fā)端向TCP收端發(fā)送的數(shù)據(jù)字節(jié)流,它表示在這個報文段中的的第一個數(shù)據(jù)字節(jié)在數(shù)據(jù)流中的序號;主要用來解決網(wǎng)絡(luò)報亂序的問題;
- Acknowledgment Number:32位確認(rèn)序列號包含發(fā)送確認(rèn)的一端所期望收到的下一個序號,因此,確認(rèn)序號應(yīng)當(dāng)是上次已成功收到數(shù)據(jù)字節(jié)序號加1。不過,只有當(dāng)標(biāo)志位中的ACK標(biāo)志(下面介紹)為1時該確認(rèn)序列號的字段才有效。主要用來解決不丟包的問題;
- Offset:給出首部中32 bit字的數(shù)目,需要這個值是因為任選字段的長度是可變的。這個字段占4bit(最多能表示15個32bit的的字,即4*15=60個字節(jié)的首部長度),因此TCP最多有60字節(jié)的首部。然而,沒有任選字段,正常的長度是20字節(jié);
- TCP Flags:TCP首部中有6個標(biāo)志比特,它們中的多個可同時被設(shè)置為1,主要是用于操控TCP的狀態(tài)機的,依次為
URG,ACK,PSH,RST,SYN,FIN。每個標(biāo)志位的意思如下:- URG:此標(biāo)志表示TCP包的緊急指針域(后面馬上就要說到)有效,用來保證TCP連接不被中斷,并且督促中間層設(shè)備要盡快處理這些數(shù)據(jù);
- ACK:此標(biāo)志表示應(yīng)答域有效,就是說前面所說的TCP應(yīng)答號將會包含在TCP數(shù)據(jù)包中;有兩個取值:0和1,為1的時候表示應(yīng)答域有效,反之為0; TCP協(xié)議規(guī)定,只有ACK=1時有效,也規(guī)定連接建立后所有發(fā)送的報文的ACK必須為1
- PSH:這個標(biāo)志位表示Push操作。所謂Push操作就是指在數(shù)據(jù)包到達(dá)接收端以后,立即傳送給應(yīng)用程序,而不是在緩沖區(qū)中排隊;
- RST:這個標(biāo)志表示連接復(fù)位請求。用來復(fù)位那些產(chǎn)生錯誤的連接,也被用來拒絕錯誤和非法的數(shù)據(jù)包;
- SYN:表示同步序號,用來建立連接。
SYN標(biāo)志位和ACK標(biāo)志位搭配使用,當(dāng)連接請求的時候,SYN=1,ACK=0;連接被響應(yīng)的時候,SYN=1,ACK=1;這個標(biāo)志的數(shù)據(jù)包經(jīng)常被用來進(jìn)行端口掃描。掃描者發(fā)送一個只有SYN的數(shù)據(jù)包,如果對方主機響應(yīng)了一個數(shù)據(jù)包回來 ,就表明這臺主機存在這個端口;但是由于這種掃描方式只是進(jìn)行TCP三次握手的第一次握手,因此這種掃描的成功表示被掃描的機器不很安全,一臺安全的主機將會強制要求一個連接嚴(yán)格的進(jìn)行TCP的三次握手; - FIN: 表示發(fā)送端已經(jīng)達(dá)到數(shù)據(jù)末尾,也就是說雙方的數(shù)據(jù)傳送完成,沒有數(shù)據(jù)可以傳送了,發(fā)送
FIN標(biāo)志位的TCP數(shù)據(jù)包后,連接將被斷開。這個標(biāo)志的數(shù)據(jù)包也經(jīng)常被用于進(jìn)行端口掃描。
- Window:窗口大小
三次握手
三次握手圖解

- 第一次握手:建立連接。客戶端發(fā)送連接請求報文段,將SYN位置為1,Sequence Number為x;然后,客戶端進(jìn)入SYN_SEND狀態(tài),等待服務(wù)器的確認(rèn);
- 第二次握手:服務(wù)器收到SYN報文段。服務(wù)器收到客戶端的SYN報文段,需要對這個SYN報文段進(jìn)行確認(rèn),設(shè)置Acknowledgment Number為x+1(Sequence Number+1);同時,自己自己還要發(fā)送SYN請求信息,將SYN位置為1,Sequence Number為y;服務(wù)器端將上述所有信息放到一個報文段(即SYN+ACK報文段)中,一并發(fā)送給客戶端,此時服務(wù)器進(jìn)入SYN_RECV狀態(tài);
- 第三次握手:客戶端收到服務(wù)器的SYN+ACK報文段。然后將Acknowledgment Number設(shè)置為y+1,向服務(wù)器發(fā)送ACK報文段,這個報文段發(fā)送完畢以后,客戶端和服務(wù)器端都進(jìn)入ESTABLISHED狀態(tài),完成TCP三次握手。
完成了三次握手,客戶端和服務(wù)器端就可以開始傳送數(shù)據(jù)。
為什么要三次握手呢?
防止已失效的連接請求報文突然又傳送到了服務(wù)端。
所謂“已失效的連接請求報文”是這樣產(chǎn)生的。
- 考慮一種正常的情況
客戶端發(fā)出連接請求,但因連接請求報文丟失而為收到確認(rèn)。于是客戶端再重傳一次連接請求。后來收到了確認(rèn),建立了連接。數(shù)據(jù)傳送完畢后,就釋放了連接??蛻舳斯舶l(fā)送了兩個連接請求報文,其中一個丟失,第二個到達(dá)了B。沒有“已失效的連接請求報文” - 假定出現(xiàn)了一種異常的情況
即客戶端發(fā)出的第一個連接請求報文段并沒有丟失,而是在某些網(wǎng)絡(luò)節(jié)點長時間滯留了,以致延誤到連接釋放以后的某個時間才到達(dá)了服務(wù)端。本來這事一個早已失效的報文。但服務(wù)端收到失效的請求報文后,就誤以為是A又發(fā)出的一個新的連接請求。于是就向客戶端發(fā)出確認(rèn)報文,同意建立連接。如果不采用三次握手,則服務(wù)端發(fā)出確認(rèn),新的連接就建立了。
由于客戶度并沒有發(fā)出建立連接的請求,因此不會理睬服務(wù)端的去人,也不會向服務(wù)端發(fā)送數(shù)據(jù)。但服務(wù)端缺以為新的連接已經(jīng)建立,并一直等待客戶端發(fā)來請求。那么服務(wù)端的資源就拜拜浪費了
采用三次握手的辦法可以防止上述現(xiàn)象的發(fā)生。在剛才的情況下,客戶端不會向服務(wù)端的確認(rèn)發(fā)出確認(rèn)。服務(wù)端收不到確認(rèn),就知道客戶度沒有要求建立連接。
四次分手
TCP連接時全雙工的,因此,需要TCP兩端要單獨執(zhí)行關(guān)閉。簡單來說A、B兩端,A提出斷開連接,則發(fā)包告訴B,讓B不要發(fā)數(shù)據(jù)了。B收到后就對A做一個回應(yīng),然后再發(fā)一個包給A,告訴A不要再發(fā)數(shù)據(jù)了,B就關(guān)閉自己的發(fā)送通道,A收到B的關(guān)閉請求后,也做出回應(yīng)。然后關(guān)閉自己的發(fā)送通道。
四次分手圖解

當(dāng)客戶端沒有數(shù)據(jù)要發(fā)送是就要釋放客戶端這邊的請求,客戶端會發(fā)送一個報文(沒有數(shù)據(jù)),F(xiàn)lags設(shè)置為FIN=1。服務(wù)端收到報文后先響應(yīng)一個報文,接著再向客戶端請求連接釋放,也是FIN??蛻舳耸盏交貜?fù)后再回復(fù)一個確認(rèn)信息,并進(jìn)入TIME_WAIT狀態(tài),等待2MSL時間。
- 第一次分手:主機1(可以使客戶端,也可以是服務(wù)器端),設(shè)置Sequence Number和Acknowledgment Number,向主機2發(fā)送一個FIN報文段;此時,主機1進(jìn)入FIN_WAIT_1狀態(tài);這表示主機1沒有數(shù)據(jù)要發(fā)送給主機2了;
- 第二次分手:主機2收到了主機1發(fā)送的FIN報文段,向主機1回一個ACK報文段,Acknowledgment Number為Sequence Number加1;主機1進(jìn)入FIN_WAIT_2狀態(tài);主機2告訴主機1,我“同意”你的關(guān)閉請求;
- 第三次分手:主機2向主機1發(fā)送FIN報文段,請求關(guān)閉連接,同時主機2進(jìn)入LAST_ACK狀態(tài);
- 第四次分手:主機1收到主機2發(fā)送的FIN報文段,向主機2發(fā)送ACK報文段,然后主機1進(jìn)入TIME_WAIT狀態(tài);主機2收到主機1的ACK報文段以后,就關(guān)閉連接;此時,主機1等待2MSL后依然沒有收到回復(fù),則證明Server端已正常關(guān)閉,那好,主機1也可以關(guān)閉連接了。
其他情況:
服務(wù)端向客戶端發(fā)送 FIN = 1 的釋放連接請求,但這個報文丟失了, A沒有接到不會發(fā)送確認(rèn)信息, 服務(wù)端 超時會重傳,這時客戶端 WAIT_TIME 還能夠接收到這個請求,這時再回復(fù)一個確認(rèn)就行了。(客戶端收到 FIN = 1 的請求后 WAIT_TIME會重新記時)
另外服務(wù)端存在一個保活狀態(tài),即如果客戶端突然故障死機了,那服務(wù)端那邊的連接資源什么時候能釋放呢? 就是保活時間到了后,服務(wù)端會發(fā)送探測信息, 以決定是否釋放連接。
注意:這里我們假設(shè)是客戶端連接斷開的發(fā)起方,如果是服務(wù)端,流程還是一樣。
TCP的半關(guān)閉
TCP提供了連接的一端在結(jié)束它的發(fā)送后還能接收來自另一端數(shù)據(jù)的能力,這就是TCP的半關(guān)閉。
半關(guān)閉的產(chǎn)生:
客戶端發(fā)送FIN,另一端發(fā)送對這個FIN的ACK報文段。 此時客戶端就處于半關(guān)閉。
實戰(zhàn)抓包演示(Wireshark)
- 這里使用我們使用的代碼為linux下Socket編程(一)中的代碼。
- 為了更清楚的看到連接的過程,我們還需要進(jìn)行抓包,這里使用Wireshark。
操作步驟:
- 打開Wireshark,進(jìn)入抓包狀態(tài)
- 設(shè)置過濾規(guī)則
tcp and tcp.port==9999 and ip.src ==192.166.11.14 or ip.dst==192.166.11.14。我這里演示的服務(wù)端ip是192.166.11.14。 - 啟動服務(wù)端程序

- 啟動客戶端然后連接。這里我們直接使用windows的telnet服務(wù)。
telnet 192.166.11.14 9999 - 連接后一次輸入'a' 'b'。發(fā)送兩次數(shù)據(jù)
- ctrl+c 關(guān)閉服務(wù)端。
抓包結(jié)果

第一次握手:
這里的截圖我們只接到傳輸層的協(xié)議部分。
向著服務(wù)端發(fā)送一個Syn的報文。其中Sequence Number=0

第二次握手:
服務(wù)端向客戶端發(fā)一個Acknowledgment和Syn的報文。Acknowledgment number=1
Sequence number =0

第三次握手:
客戶端發(fā)向服務(wù)端一個Acknowledgment的報文。Acknowledgment number=1
Sequence number =1
然后連接建立完成

第一次發(fā)送數(shù)據(jù)

\
第二次發(fā)送數(shù)據(jù)

第一次分手:

第二次分手:

第三次分手

第四次分手
