1. 程式人生 > >java 網路程式設計——TCP 的三次握手和四次揮手

java 網路程式設計——TCP 的三次握手和四次揮手

文章目錄


在TCP/IP協議中,TCP協議提供可靠的連線服務,採用三次握手建立一個連線.

TCP報頭

在這裡插入圖片描述
埠號:用來標識同一臺計算機的不同的應用程序。

源埠:表示傳送端埠,源埠和IP地址的作用是標識報文的返回地址。

目的埠:表示接收端埠。

TCP報頭中的源埠號和目的埠號同IP資料報中的源IP與目的IP唯一確定一條TCP連線。

序號和確認序號:是TCP可靠傳輸的關鍵部分。序號是本報文段傳送的資料組的第一個位元組的序號。在TCP傳送的流中,每一個位元組一個序號。
e.g.一個報文段的序號為300,此報文段資料部分共有100位元組,則下一個報文段的序號為400。所以序號確保了TCP傳輸的有序性。
確認號

:即ACK,指明下一個期待收到的位元組序號,表明該序號之前的所有資料已經正確無誤的收到。確認號只有當ACK標誌為1時才有效。比如建立連線時,SYN報文的ACK標誌位為0。

資料偏移/首部長度:4bits。由於首部可能含有可選項內容,因此TCP報頭的長度是不確定的,報頭不包含任何任選欄位則長度為20位元組,4位首部長度欄位所能表示的最大值為1111,轉化為10進製為15,15*32/8 = 60,故報頭最大長度為60位元組。首部長度也叫資料偏移,是因為首部長度實際上指示了資料區在報文段中的起始偏移值。

保留:為將來定義新的用途保留,現在一般置0。

控制位:URG ACK PSH RST SYN FIN

,共6個,每一個標誌位表示一個控制功能。

URG:緊急指標標誌,為1時表示緊急指標有效,為0則忽略緊急指標。

ACK:確認序號標誌,為1時表示確認號有效,為0表示報文中不含確認資訊,忽略確認號欄位。

PSH:push標誌,為1表示是帶有push標誌的資料,指示接收方在接收到該報文段以後,應儘快將這個報文段交給應用程式,而不是在緩衝區排隊。

RST:重置連線標誌,用於重置由於主機崩潰或其他原因而出現錯誤的連線。或者用於拒絕非法的報文段和拒絕連線請求。

SYN:同步序號,用於建立連線過程,在連線請求中,SYN=1和ACK=0表示該資料段沒有使用捎帶的確認域,而連線應答捎帶一個確認,即SYN=1和ACK=1。

FIN:finish標誌,用於釋放連線,為1時表示傳送方已經沒有資料傳送了,即關閉本方資料流。

視窗:滑動視窗大小,用來告知傳送端接受端的快取大小,以此控制傳送端傳送資料的速率,從而達到流量控制。視窗大小時一個16bit欄位,因而視窗大小最大為65535。

校驗和:奇偶校驗,此校驗和是對整個的 TCP 報文段,包括 TCP 頭部和 TCP 資料,以 16 位字進行計算所得。由傳送端計算和儲存,並由接收端進行驗證。

緊急指標:只有當 URG 標誌置 1 時緊急指標才有效。緊急指標是一個正的偏移量,和順序號欄位中的值相加表示緊急資料最後一個位元組的序號。 TCP 的緊急方式是傳送端向另一端傳送緊急資料的一種方式。

選項和填充:最常見的可選欄位是最長報文大小,又稱為MSS(Maximum Segment Size),每個連線方通常都在通訊的第一個報文段(為建立連線而設定SYN標誌為1的那個段)中指明這個選項,它表示本端所能接受的最大報文段的長度。選項長度不一定是32位的整數倍,所以要加填充位,即在這個欄位中加入額外的零,以保證TCP頭是32的整數倍。

資料部分: TCP 報文段中的資料部分是可選的。在一個連線建立和一個連線終止時,雙方交換的報文段僅有 TCP 首部。如果一方沒有資料要傳送,也使用沒有任何資料的首部來確認收到的資料。在處理超時的許多情況中,也會發送不帶任何資料的報文段。

三次握手

在這裡插入圖片描述

第一次握手
客戶端將標誌位SYN置為1,隨機產生一個值seq=x,並將該資料包傳送給伺服器,客戶端進入SYN_SENT狀態,等待伺服器確認。(其中,SYN=1,ACK=0,表示這是一個TCP連線請求資料報文;序號seq=x,表明傳輸資料時的第一個資料位元組的序號是x)。

第二次握手
伺服器收到請求後,必須確認客戶的資料包。同時自己也傳送一個SYN包,即SYN+ACK包,此時伺服器進入SYN_RECV狀態。(其中確認報文段中,標識位SYN=1,ACK=1,表示這是一個TCP連線響應資料報文,並含服務端的初始序號seq(伺服器)=y,以及伺服器對客戶端初始序號的確認號ack(伺服器)=seq(客戶端)+1=x+1)

第三次握手
客戶端收到伺服器的SYN+ACK包,向伺服器傳送一個序列號(seq=x+1),確認號為ack(客戶端)=y+1,此包傳送完畢,客戶端和伺服器進入ESTAB_LISHED(TCP連線成功)狀態,完成三次握手。

未連線佇列

在三次握手協議中,伺服器維護一個未連線佇列,該佇列為每個客戶端的SYN包(syn=j)開設一個條目,該條目表明伺服器已收到SYN包,並向客戶發出確認,正在等待客戶的確認包時,刪除該條目,伺服器進入ESTAB_LISHED狀態。

四次揮手

在這裡插入圖片描述
第一次揮手
首先,客戶端傳送一個FIN,用來關閉客戶端到伺服器的資料傳送,然後等待伺服器的確認。其中終止標誌位FIN=1,序列號seq=u。

第二次揮手
伺服器收到這個FIN,它傳送一個ACK,確認ack為收到的序號加一(ack=seq+1=u+1)。

第三次揮手
伺服器傳送一個FIN,用來關閉伺服器端到客戶端的數傳送。

第四次揮手
客戶端收到FIN後,併發回一個ACK報文確認,並將確認序號seq設定為收到序號加一。首先進行關閉的一方將執行主動關閉,而另一方執行被動關閉。

客戶端傳送FIN後,進入終止等待狀態,伺服器收到客戶端連線釋放報文段後,就立即給客戶端傳送確認,伺服器就進入CLOSE_WAIT狀態,此時TCP伺服器程序就通知高層應用程序,因而從客戶端到伺服器的連線就釋放了。此時是“半關閉狀態”,即客戶端不可以傳送給伺服器,伺服器可以傳送給客戶端。
此時,如果伺服器沒有資料報傳送給客戶端,其應用程式就通知TCP釋放連線,然後傳送給客戶端連線釋放資料報,並等待確認。客戶端傳送確認後,進入TIME_WAIT狀態,但是此時TCP連線還沒有釋放,然後經過等待計時器設定的2MSL後,才進入到CLOSE狀態。

狀態詳解

CLOSED: 這個沒什麼好說的了,表示初始狀態。

LISTEN: 這個也是非常容易理解的一個狀態,表示伺服器端的某個SOCKET處於監聽狀態,可以接受連線了。

SYN_RCVD: 這個狀態表示接受到了SYN報文,在正常情況下,這個狀態是伺服器端的SOCKET在建立TCP連線時的三次握手會話過程中的一箇中間狀態,很短暫,基本 上用netstat你是很難看到這種狀態的,除非你特意寫了一個客戶端測試程式,故意將三次TCP握手過程中最後一個ACK報文不予傳送。因此這種狀態 時,當收到客戶端的ACK報文後,它會進入到ESTABLISHED狀態。

SYN_SENT: 這個狀態與SYN_RCVD遙想呼應,當客戶端SOCKET執行CONNECT連線時,它首先發送SYN報文,因此也隨即它會進入到了SYN_SENT狀態,並等待服務端的傳送三次握手中的第2個報文。SYN_SENT狀態表示客戶端已傳送SYN報文。

**ESTABLISHED:**這個容易理解了,表示連線已經建立了。

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看到。

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連線。

CLOSE_WAIT: 這種狀態的含義其實是表示在等待關閉。怎麼理解呢?當對方close一個SOCKET後傳送FIN報文給自己,你係統毫無疑問地會迴應一個ACK報文給對 方,此時則進入到CLOSE_WAIT狀態。接下來呢,實際上你真正需要考慮的事情是察看你是否還有資料傳送給對方,如果沒有的話,那麼你也就可以 close這個SOCKET,傳送FIN報文給對方,也即關閉連線。所以你在CLOSE_WAIT狀態下,需要完成的事情是等待你去關閉連線。

LAST_ACK: 這個狀態還是比較容易好理解的,它是被動關閉一方在傳送FIN報文後,最後等待對方的ACK報文。當收到ACK報文後,也即可以進入到CLOSED可用狀態了。