衆所周知,tcp要3次握手來建立連接配接,而斷開連接配接卻需要4次揮手,為何需要4次揮手,同時需要那麼多的wait狀态,比如wait1和wait2狀态,那是因為在建立連接配接的時候,收發雙方都是“純淨”的,用戶端發送syn的時候,伺服器并沒有什麼資料要發送,而隻是需要發送一個synack即可,是以連接配接開始的握手所傳輸的都是控制資料。反觀連接配接結束的握手則不然,由于tcp是全雙工的,且連接配接結束必須由一方發起,是以當一方發起連接配接結束的握手時,另一方可能正有資料要發給發起結束握手的一方,而fin的ack則可以由這個資料捎帶過去,或者以别的方式發出ack。接收fin的程序可能根本就沒有在receive或者send中,那麼此時接收端的ack肯定是協定棧發送的,但是對方關閉了tcp這個消息必須想辦法讓應用程式知道後再由應用程式決定如何做,是以不能指望fin的ack一定有另一個fin,以socket的實作為例,建立連接配接的時候,如果用戶端connect,那麼伺服器端一定有一個處于listen狀态的socket在等待接收用戶端的syn封包,但是對于結束連接配接,用戶端和伺服器端完全就對等了,不能指望另一端一定在等待接收fin,是以每一端都要顯式發送fin。
針對是主動發送fin還是被動接收fin後再發送fin,tcp連接配接關閉時兩端的狀态機轉換并不同,主動發送fin的一端在發出fin後進入wait1狀态,然後在收到對端的ack後進入wait2狀态,在wait2中如果收到了對端的fin,那麼就會進入time-wait狀态,之是以有一個wait2和time-wait,是因為對端在收到fin和關閉連接配接之間有一個時間差并且可能最後的一個ack會丢失進而對端重發fin,在些情況下,可能對端的發送緩沖區中的資料還沒有發出,也可能對端會重發fin,總之tcp的可靠性在此時已經不能發揮作用了,是以就隻有讓時間來沖淡一切了。當然time-wait狀态以及waitX狀态都可以設定逾時時間,逾時時間一旦過去,連接配接将被切斷,資源将被回收。
最後舉一例來說明連接配接的斷開階段的問題:client主動斷開後經過了一系列階段後最終進入了time-wait,而server發送給client的一個序列号為s資料d被中間路由器暫存了,client的time-wait逾時,此時client又發起了一個對相同server的連接配接(位址/端口相同),三次握手過後,被暫存的資料d終于被路由器發送到了client,此時client正要接收序列号為s的資料,那麼豈不混亂了?如果恰好這個被暫存的資料是前一次server由于沒有收到client最後的ack而重新發送的fin分組,那麼client就會認為是server斷開了連接配接...是以time-wait是必要的,并且這也解釋了為何在client發起連接配接的時候,序列号要随機生成。
本文轉自 dog250 51CTO部落格,原文連結:http://blog.51cto.com/dog250/1271784