TCP報文格式
TCP提供一種面向連接的,可靠的字節流服務。
TCP首部的數據格式如下。(如果不計任選字段,通常是20個字節)
字段分析
源端口:源端口和IP地址的作用是標識報文的返回地址。
目的端口:端口指明接收方計算機上的應用程序接口。
TCP報頭中的源端口號和目的端口號同IP數據報中的源IP與目的IP唯一確定一條TCP連接。
序號:是TCP可靠傳輸的關鍵部分。序號是該報文段發送的數據組的第一個字節的序號。在TCP傳送的流中,每一個字節都有一個序號。比如一個報文段的序號為300,報文段數據部分共有100字節,則下一個報文段的序號為400。所以序號確保了TCP傳輸的有序性。
確認號:即ACK,指明下一個期待收到的字節序號,表明該序號之前的所有數據已經正確無誤的收到。確認號只有當ACK標志為1時才有效。比如建立連接時,SYN報文的ACK標志位為0。
首部長度/數據偏移:占4位,它指出TCP報文的數據距離TCP報文段的起始處有多遠。由于首部可能含有可選項內容,因此TCP報頭的長度是不確定的,報頭不包含任何任選字段則長度為20字節,4位首部長度字段所能表示的最大值為1111,轉化為10進制為15,15*32/8=60,故報頭最大長度為60字節。首部長度也叫數據偏移,是因為首部長度實際上指示了數據區在報文段中的起始偏移值。
保留:占6位,保留今后使用,但目前應都位0。
控制位:URG ACK PSH RST SYN FIN,共6個,每一個標志位表示一個控制功能。
緊急URG:當URG=1,表明緊急指針字段有效。告訴系統此報文段中有緊急數據
確認ACK:僅當ACK=1時,確認號字段才有效。TCP規定,在連接建立后所有報文的傳輸都必須把ACK置1。
推送PSH:當兩個應用進程進行交互式通信時,有時在一端的應用進程希望在鍵入一個命令后立即就能收到對方的響應,這時候就將PSH=1。
復位RST:當RST=1,表明TCP連接中出現嚴重差錯,必須釋放連接,然后再重新建立連接。
同步SYN:在連接建立時用來同步序號。當SYN=1,ACK=0,表明是連接請求報文,若同意連接,則響應報文中應該使SYN=1,ACK=1。
終止FIN:用來釋放連接。當FIN=1,表明此報文的發送方的數據已經發送完畢,并且要求釋放。
窗口:滑動窗口大小,用來告知發送端接受端的緩存大小,以此控制發送端發送數據的速率,從而達到流量控制。窗口大小時一個16bit字段,因而窗口大小最大為65535。
校驗和:奇偶校驗,此校驗和是對整個的 TCP 報文段,包括 TCP 頭部和 TCP 數據,以 16 位字進行計算所得。由發送端計算和存儲,并由接收端進行驗證。
緊急指針:只有當 URG 標志置 1 時緊急指針才有效。緊急指針是一個正的偏移量,和順序號字段中的值相加表示緊急數據最后一個字節的序號。 TCP 的緊急方式是發送端向另一端發送緊急數據的一種方式。
選項和填充:最常見的可選字段是最長報文大小,又稱為MSS(Maximum Segment Size),每個連接方通常都在通信的第一個報文段(為建立連接而設置SYN標志為1的那個段)中指明這個選項,它表示本端所能接受的最大報文段的長度。選項長度不一定是32位的整數倍,所以要加填充位,即在這個字段中加入額外的零,以保證TCP頭是32的整數倍。
數據部分:?TCP 報文段中的數據部分是可選的。在一個連接建立和一個連接終止時,雙方交換的報文段僅有 TCP 首部。如果一方沒有數據要發送,也使用沒有任何數據的首部來確認收到的數據。在處理超時的許多情況中,也會發送不帶任何數據的報文段。
三次握手
第一次握手:客戶端發送syn包(syn=x)到服務器,并進入SYN_SEND狀態,等待服務器確認;
第二次握手:服務器收到syn包,必須確認客戶的SYN(ack=x+1),同時自己也發送一個SYN包(syn=y),即SYN+ACK包,此時服務器進入SYN_RECV狀態;
第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=y+1),此包發送完畢,客戶端和服務器進入ESTABLISHED狀態,完成三次握手。
握手過程中傳送的包里不包含數據,三次握手完畢后,客戶端與服務器才正式開始傳送數據。理想狀態下,TCP連接一旦建立,在通信雙方中的任何一方主動關閉連接之前,TCP 連接都將被一直保持下去。
為什么會采用三次握手,若采用二次握手可以嗎? 四次呢?
建立連接的過程是利用客戶服務器模式,假設主機A為客戶端,主機B為服務器端。
采用三次握手是為了防止失效的連接請求報文段突然又傳送到主機B,因而產生錯誤。
失效的連接請求報文段是指:主機A發出的連接請求沒有收到主機B的確認,于是經過一段時間后,主機A又重新向主機B發送連接請求,且建立成功,順序完成數據傳輸。考慮這樣一種特殊情況,主機A第一次發送的連接請求并沒有丟失,而是因為網絡節點導致延遲達到主機B,主機B以為是主機A又發起的新連接,于是主機B同意連接,并向主機A發回確認,但是此時主機A根本不會理會,主機B就一直在等待主機A發送數據,導致主機B的資源浪費。
采用兩次握手不行,原因就是上面說的失效的連接請求的特殊情況。而在三次握手中, client和server都有一個發syn和收ack的過程, 雙方都是發后能收, 表明通信則準備工作OK。
為什么不是四次握手呢? 大家應該知道通信中著名的藍軍紅軍約定, 這個例子說明, 通信不可能100%可靠, 而上面的三次握手已經做好了通信的準備工作, 再增加握手, 并不能顯著提高可靠性, 而且也沒有必要。
四次揮手
數據傳輸完畢后,雙方都可釋放連接。最開始的時候,客戶端和服務器都是處于ESTABLISHED狀態,假設客戶端主動關閉,服務器被動關閉。
第一次揮手:客戶端發送一個FIN,用來關閉客戶端到服務器的數據傳送,也就是客戶端告訴服務器:我已經不 會再給你發數據了(當然,在fin包之前發送出去的數據,如果沒有收到對應的ack確認報文,客戶端依然會重發這些數據),但是,此時客戶端還可 以接受數據。
FIN=1,其序列號為seq=u(等于前面已經傳送過來的數據的最后一個字節的序號加1),此時,客戶端進入FIN-WAIT-1(終止等待1)狀態。 TCP規定,FIN報文段即使不攜帶數據,也要消耗一個序號。
第二次揮手:服務器收到FIN包后,發送一個ACK給對方并且帶上自己的序列號seq,確認序號為收到序號+1(與SYN相同,一個FIN占用一個序號)。此時,服務端就進入了CLOSE-WAIT(關閉等待)狀態。TCP服務器通知高層的應用進程,客戶端向服務器的方向就釋放了,這時候處于半關閉狀態,即客戶端已經沒有數據要發送了,但是服務器若發送數據,客戶端依然要接受。這個狀態還要持續一段時間,也就是整個CLOSE-WAIT狀態持續的時間。
此時,客戶端就進入FIN-WAIT-2(終止等待2)狀態,等待服務器發送連接釋放報文(在這之前還需要接受服務器發送的最后的數據)。
第三次揮手:服務器發送一個FIN,用來關閉服務器到客戶端的數據傳送,也就是告訴客戶端,我的數據也發送完了,不會再給你發數據了。由于在半關閉狀態,服務器很可能又發送了一些數據,假定此時的序列號為seq=w,此時,服務器就進入了LAST-ACK(最后確認)狀態,等待客戶端的確認。
第四次揮手:主動關閉方收到FIN后,發送一個ACK給被動關閉方,確認序號為收到序號+1,此時,客戶端就進入了TIME-WAIT(時間等待)狀態。注意此時TCP連接還沒有釋放,必須經過2?MSL(最長報文段壽命)的時間后,當客戶端撤銷相應的TCB后,才進入CLOSED狀態。
服務器只要收到了客戶端發出的確認,立即進入CLOSED狀態。同樣,撤銷TCB后,就結束了這次的TCP連接。可以看到,服務器結束TCP連接的時間要比客戶端早一些。至此,完成四次揮手。
為什么客戶端最后還要等待2MSL?
MSL(Maximum Segment Lifetime),TCP允許不同的實現可以設置不同的MSL值。
第一,保證客戶端發送的最后一個ACK報文能夠到達服務器,因為這個ACK報文可能丟失,站在服務器的角度看來,我已經發送了FIN+ACK報文請求斷開了,客戶端還沒有給我回應,應該是我發送的請求斷開報文它沒有收到,于是服務器又會重新發送一次,而客戶端就能在這個2MSL時間段內收到這個重傳的報文,接著給出回應報文,并且會重啟2MSL計時器。
第二,防止類似與“三次握手”中提到了的“已經失效的連接請求報文段”出現在本連接中。客戶端發送完最后一個確認報文后,在這個2MSL時間中,就可以使本連接持續的時間內所產生的所有報文段都從網絡中消失。這樣新的連接中不會出現舊連接的請求報文。
為什么建立連接是三次握手,關閉連接確是四次揮手呢?
建立連接的時候, 服務器在LISTEN狀態下,收到建立連接請求的SYN報文后,把ACK和SYN放在一個報文里發送給客戶端。
而關閉連接時,服務器收到對方的FIN報文時,僅僅表示對方不再發送數據了但是還能接收數據,而自己也未必全部數據都發送給對方了,所以己方可以立即關閉,也可以發送一些數據給對方后,再發送FIN報文給對方來表示同意現在關閉連接,因此,己方ACK和FIN一般都會分開發送,從而導致多了一次。
轉自:https://www.javazhiyin.com/1693.html