2016-04-14 21:23:00socket
TCP一共有11種連接狀態:spa
SYN_SENT:在發送連接請求後等待匹配的連接請求(客戶端向伺服器端發送SYN請求創建一個連接,之後將狀態置為SYN_SENT)同步
FIN_WAIT_1:等待遠程TCP連接中斷請求,或先前的連接中斷請求的確認(主動關閉端發送FIN請求主動關閉連接,之後將狀態置為FIN_WAIT_1)im
FIN_WAIT_2:從遠程TCP等待連接中斷請求(主動關閉端接到ACK後,之後將狀態置為FIN_WAIT_2,此時是半關閉狀態,即主動關閉端還能夠接受數據,可是無法發送數據 )img
CLOSING:等待遠程TCP對連接中斷的確認(若是兩端同時發送FIN,則在發送後兩端都進入FIN_WAIT_1狀態。在收到對端的FIN後回復ACK報文,之後將狀態置為CLOSING,比較少見)推送
TIME_WAIT:等待足夠的時間以確保遠程TCP接收到連接中斷請求的確認(主動關閉端接收到FIN,TCP就發送ACK包,之後將狀態置為TIME_WAIT)wireshark
ADVERTISEMENTsync
LISTEN:偵聽來自遠方的TCP埠的連接請求(伺服器端需打開一個socket進行監聽,之後將狀態置為LISTEN)time
SYN_RCVD:在收到和發送一個連接請求後等待對方對連接請求的確認(當伺服器端收到客戶端的連接請求後,將標誌位ACK和SYN置為1發送給客戶端,之後將狀態置為SYN_RCVD)push
CLOSE_WAIT:等待從本地用戶發來的連接中斷請求(被動關閉端接到FIN後,就發出ACK以回應FIN請求,之後將狀態置為CLOSE_WAIT)
LAST_ACK:等待原來的發向遠程TCP的連接中斷請求的確認(被動關閉端在狀態為CLOSE_WAIT一段時間後,將餘下數據傳送完後發回一個FIN請求關閉連接,之後將狀態置為LAST_ACK)
CLOSED:沒有任何連接狀態(被動關閉端在接受到主動關閉端ACK包,之後將狀態置為CLOSED,連接結束)
ESTABLISHED:表明一個打開的連接(成功創建連接,之後將狀態置為ESTABLISHED,開始傳輸數據)
ADVERTISEMENT
其中1-5是客戶端獨有的狀態,6-9為服務端獨有的狀態,而10-11是客戶端服務端公有狀態。
再插入的說一下TCP的六個標誌位,按在TCP首部中的順序依次是:
1.URG(urgent緊急數據)
2.ACK(acknowlegment確認)
3.PSH(push推送)
4.RST(reset重置)
5.SYN(synchronized創建聯機同步序號)
6.FIN(finish結束)
須要哪種類型則將該標誌位致為1。
下圖就是比較經典的TCP狀態轉換圖:
接下來就分別通過三次握手(連接創建)和四次揮手(連接釋放)說明下狀態轉化圖。
三次握手:
創建連接前,客戶端和服務端狀態都為CLOSED,服務端創建socket被動打開連接,並將狀態置為LISTEN。
客戶端主動打開連接,向服務端發送SYN報文,將TCP首部控制位中的SYN置為1,同時序號seq為客戶端隨機生成的一個初始序號seq = x(SYN報文段不能攜帶數據,但要消耗一個序列號),並將客戶端狀態置為SYN_SENT。
服務端收到SYN報文後,如贊成創建連接,則向客戶端發送SYN+ACK報文,將TCP首部控制位中的SYN和ACK都置為1,確認號為ack = x + 1,同時隨機生成一個本身的序號seq = y,並將服務端狀態致為SYN_RCVD(同步收到狀態)。
客戶端收到SYN+ACK報文後,還需向服務端發送ACK報文,將TCP首部控制位中的ACK置為1,確認號ack = y + 1,序列號seq = x + 1,並將客戶端狀態置為ESTABLISHED。
服務端收到ACK報文後,也將狀態置為ESTABLISHED,此時三次握手完成,連接創建,進入數據傳送狀態。
為什麼要三次握手而不是兩次:
現假定出現一種異常情況:客戶端發送的SYN報文並未丟失,而是在某個網絡節點長時間滯留了,以至延誤到客戶端連接釋放後的某個時間纔到達服務端,但服務端收到此失效的SYN報文後,誤以為是客戶端又發出的一次新的連接請求,於是就向客戶端發出SYN+ACK報文段,贊成創建連接,假定不採用三次握手而是兩次握手,服務端就認為連接已經創建,並一直等待客戶端發送數據,但由於這次連接在客戶端已經失效了,所以不會對服務端的SYN+ACK報文給予回應,這就形成了服務端許多資源的白白浪費,全部纔有三次握手的辦法能夠防止上述現象的發生,即防止已失效的SYN報文忽然又傳回服務端,因為產生錯誤。例如上述的情況,客戶端不向服務端發回ACK報文,由於客戶端收不到ACK報文,就知道客戶端並未要求創建連接,也不會形成資源的白白浪費,但三次握手也是有必定缺陷的,會被利用發起SYN flood攻擊。
四次揮手:
TCP連接是全雙工的,因此釋放連接會比創建連接略微複雜,釋放連接前,兩端都處於ESTABLISHED數據傳輸狀態。
客戶端先向伺服器端發送FIN報文,將TCP首部控制位中的FIN置為1,其序號seq = u,u等於客戶端前面最後一個已傳送過的包seq,並將狀態置為FIN_WAIT_1,等待服務端的確認。
服務端收到FIN報文後,將發送ACK報文,確認號ack = u + 1,序號seq = v,v等於服務端前面最後一個已傳送過的包的seq,之後將狀態致為CLOSE_WAIT(此時TCP連接處於半關閉狀態,即客戶端已經沒有數據要發送了,但若服務端要發送數據,客戶端仍要接受,也就是說服務端到客戶端這個方向的連接並未關閉,這個狀態可能會持續一段時間)。
當客戶端收到服務端傳來的確認報文後,將狀態致為FIN_WATI_2,等待服務端發送的FIN+ACK報文段。
若服務端沒有要向客戶端發送的數據,其應用進程就通知TCP釋放連接,將FIN+ACK報文段TCP首部控制位FIN和ACK置為1,此時序號seq = w(因為以前可能又傳遞一些數據),確認號ack = u + 1(上次的確認號),並將狀態置為LAST_ACK。
客戶端收到服務端的FIN+ACK報文後,需發送確認報文段,確認號ack = w + 1,序列號為seq = u + 1,並將狀態置為TIME_WAIT狀態,現在TCP連接還沒有釋放掉,必須經過2MSL(Max Segment Lifetime 最長報文段壽命)後才能進入CLOSED狀態,服務端在接受到確認報文後將狀態置為CLOSED。
上述的TCP連接釋放過程是四次揮手。
為什麼四次揮手:
TCP連接是全雙工的,當關閉連接時,當收到客戶端的FIN報文時,僅僅表示客戶端沒有數據發送給服務端了;但未必服務端全部的數據都發送給客戶端了,因此服務端未必會馬上會關閉SOCKET,也即服務端可能還須要發送一些數據給客戶端之後,再發送FIN+ACK報文給客戶端來表示現在能夠關閉連接了,因此服務端這裡的ACK報文和FIN報文多數情況下都是分開發送的以確保數據能夠完成傳輸。
1.保證客戶端發送的最後一個ACK報文能夠到達服務端:
客戶端發送的最後一個ACK報文段可能會丟失,而處在LAST_ACK狀態的服務端因收不到客戶端的ACK報文會超時重傳FIN+ACK報文,所以客戶端就能在2MSL時間內收到這個重傳的FIN+ACK報文並重傳一次ACK報文,同時也從新啟動2MSL計時器,這樣客戶端服務端都能正常進入CLOSED狀態。若是客戶端不在TIME_WAIT狀態下等待一段時間,而是在發完ACK報文就進入CLOESD狀態釋放連接,那麼一旦這個ACK報文丟失,服務端收不到此ACK報文就無法正常進入CLOESD狀態。
2.防止已失效的SYN報文出現在這次連接中:
客戶端在發送完最後一個ACK報文經過2MSL後,就可使這次連接持續的時間內所產生的全部報文從網絡中消失,這樣就能確保下一次新的連接中不會出現舊的連接請求報文。
最後再結合wireshark詳細看一下三次握手,四次揮手的過程:
三次握手:
能夠看到該包為SYN包,並且seq = 54778471
該包為SYN+ACK包,ack = 54778471 + 1 = 54778472;seq = 720032937
該包為ACK包, ack = 750032937 + 1 = 750032938;seq = 54778472
至此,三次握手連接創建完成,兩端開始傳輸數據。
四次揮手:
這個是客戶端發送的最後的包,其中seq = 54779313;ack=720114520;
這個是客戶端主動發起的FIN包,seq =54779313;ack = 720114250
這是服務端回的FIN+ACK包,因為CLOSE_WAIT下沒有數據,因此兩個過程合一塊了,seq = 720114250, ack = 54779313 + 1 = 54779314
這是客戶端回的最後一個ACK包,seq = 54779314;ack = 720114250 + 1 =720114251
至此,四次握手連接釋放完成,兩端進入CLOSED狀態。
數據傳遞:
這是服務端傳送數據的一個包,seq = 720033157;ack = 54778889,len = 1440
這是客戶端收到數據後發的確認包,可見seq = 54778889;ack = 720033157 + len = 720034597
可知: 客戶端seq = 上個服務端包ack值,ack = 上個服務端包seq + len值 服務端seq = 上個客戶端包ack值,ack = 上個客戶端包seq + len值