天天看點

Linux下TCP連接配接過程總結

一、Linux伺服器上11種網絡連接配接狀态:

Linux下TCP連接配接過程總結

      圖:TCP的狀态機

通常情況下,一個正常的TCP連接配接,都會有三個階段:1、TCP三向交握; 2、資料傳送; 3、TCP四次揮手

注:以下說明最好能結合”圖:TCP的狀态機”來了解。

SYN: (同步序列編号,Synchronize Sequence Numbers)該标志僅在三次握手建立TCP連接配接時有效。表示一個新的TCP連接配接請求。

ACK: (确認編号,Acknowledgement Number)是對TCP請求的确認标志,同時提示對端系統已經成功接收所有資料。

FIN: (結束标志,FINish)用來結束一個TCP回話.但對應端口仍處于開放狀态,準備接收後續資料。

1)、LISTEN:首先服務端需要打開一個socket進行監聽,狀态為LISTEN. /* The socket is listening for incoming connections. 偵聽來自遠方TCP端口的連接配接請求 */

2)、SYN_SENT:用戶端通過應用程式調用connect進行active open.于是用戶端tcp發送一個SYN以請求建立一個連接配接.之後狀态置為SYN_SENT. /*The socket is actively attempting to establish a connection. 在發送連接配接請求後等待比對的連接配接請求 */

3)、SYN_RECV:服務端應發出ACK确認用戶端的SYN,同時自己向用戶端發送一個SYN. 之後狀态置為SYN_RECV /* A connection request has been received from the network. 在收到和發送一個連接配接請求後等待對連接配接請求的确認 */

4)、ESTABLISHED: 代表一個打開的連接配接,雙方可以進行或已經在資料互動了。/* The socket has an established connection. 代表一個打開的連接配接,資料可以傳送給使用者 */

5)、FIN_WAIT1:主動關閉(active close)端應用程式調用close,于是其TCP發出FIN請求主動關閉連接配接,之後進入FIN_WAIT1狀态./* The socket is closed, and the connection is shutting down. 等待遠端TCP的連接配接中斷請求,或先前的連接配接中斷請求的确認 */

6)、CLOSE_WAIT:被動關閉(passive close)端TCP接到FIN後,就發出ACK以回應FIN請求(它的接收也作為檔案結束符傳遞給上層應用程式),并進入CLOSE_WAIT. /* The remote end has shut down, waiting for the socket to close. 等待從本地使用者發來的連接配接中斷請求 */

7)、FIN_WAIT2:主動關閉端接到ACK後,就進入了FIN-WAIT-2 ./* Connection is closed, and the socket is waiting for a shutdown from the remote end. 從遠端TCP等待連接配接中斷請求 */

8)、LAST_ACK:被動關閉端一段時間後,接收到檔案結束符的應用程式将調用CLOSE關閉連接配接。這導緻它的TCP也發送一個 FIN,等待對方的ACK.就進入了LAST-ACK . /* The remote end has shut down, and the socket is closed. Waiting for acknowledgement. 等待原來發向遠端TCP的連接配接中斷請求的确認 */

9)、TIME_WAIT:在主動關閉端接收到FIN後,TCP就發送ACK包,并進入TIME-WAIT狀态。/* The socket is waiting after close to handle packets still in the network.等待足夠的時間以確定遠端TCP接收到連接配接中斷請求的确認 */

10)、CLOSING: 比較少見./* Both sockets are shut down but we still don’t have all our data sent. 等待遠端TCP對連接配接中斷的确認 */

11)、CLOSED: 被動關閉端在接受到ACK包後,就進入了closed的狀态。連接配接結束./* The socket is not being used. 沒有任何連接配接狀态 */

TIME_WAIT狀态的形成隻發生在主動關閉連接配接的一方。

   主動關閉方在接收到被動關閉方的FIN請求後,發送成功給對方一個ACK後,将自己的狀态由FIN_WAIT2修改為TIME_WAIT,而必須再等2 倍 的MSL(Maximum Segment Lifetime,MSL是一個資料報在internetwork中能存在的時間)時間之後雙方才能把狀态 都改為CLOSED以關閉連接配接。目前RHEL裡保持TIME_WAIT狀态的時間為60秒。當然上述很多TCP狀态在系統裡都有對應的解釋或設定,可見 man tcp

二、關于長連接配接和短連接配接:

  通俗點講,短連接配接就是一次TCP請求得到結果後,連接配接馬上結束.而長連接配接并不馬上斷開,而一直保持着,直到長連接配接TIMEOUT(具體程式都有相關參數說明).長連接配接可以避免不斷的進行TCP三向交握和四次揮手.

長 連接配接(keepalive)是需要靠雙方不斷的發送探測包來維持的,keepalive期間服務端和用戶端的TCP連接配接狀态是ESTABLISHED.目 前http 1.1版本裡預設都是keepalive(1.0版本預設是不keepalive的),ie6/7/8和firefox都預設用的是http 1.1版本了(如何檢視目前浏覽器用的是哪個版本,這裡不再贅述)。Apache,java

  一個應用至于到底是該使用短連接配接還是長連接配接,應該視具體情況而定。一般的應用應該使用長連接配接。

tcp 四次揮手

   TCP協定有一個優雅的關閉(graceful close)機制,以保證應用程式在關閉連接配接時不必擔心正在傳輸的資料會丢失。如第4.5節的壓縮示例程式所示,這個機制還設計為允許兩個方向的資料傳輸 互相獨立地終止。關閉機制的工作流程是:應用程式通過調用連接配接套接字的close()方法或shutdownOutput()方法表明資料已經發送完畢。 此刻,底層的TCP實作首先将留存在SendQ隊列中的資料傳輸出去(還要依賴于另一端RecvQ隊列的剩餘空間),然後向另一端發送一個關閉TCP連接配接 的握手消息。該關閉握手消息可以看作是流終止标志:它告訴接收端TCP不會再有新的資料傳入RecvQ隊列了。(注意,關閉握手消息本身并沒有傳遞給接收 端應用程式,而是通過read()方法傳回-1來訓示其在位元組流中的位置。)正在關閉的TCP将等待其關閉握手消息的确認資訊,該确認資訊表明在連接配接上傳 輸的所有資料已經安全地傳輸到了RecvQ中。隻要收到了确認消息,該連接配接就變成"半關閉(Half closed)"狀态。直到連接配接的另一個方向上收到了對稱的握手消息後,連接配接才完全關閉--也就是說,連接配接的兩端都表明它們再沒有資料要發送了。

   TCP連接配接的關閉事件序列可能以兩種方式發生:一種方式是先由一個應用程式調用close()方法(或shutdownOutput()方法),并在另 一端調用close()方法之前完成其關閉握手消息;另一種方式是兩端同時調用close()方法,它們的關閉握手消息在網絡上交叉傳輸。圖6.10展示 了以第一種方式關閉連接配接時,底層實作中的事件序列。關閉握手消息已經發送,套接字資料結構的狀态也已經設定為"Closing"(專業術語稱 為"FIN_WAIT_1"),然後close()調用傳回。完成這些工作後,将禁止在該Socket上的任何讀寫操作(會抛出異常)。當收到關閉握手确 認消息後,套接字資料結構的狀态則改變為"半關閉"(專業術語稱為"FIN_WAIT_2"),這種狀态将一直持續,直到接收到另一端的關閉握手消息

   關閉TCP連接配接的最後微妙之處在于對Time-Wait狀态的需要。TCP規範要求在終止連接配接時,兩端的關閉握手都完成後,至少要有一個套接字在 Time-Wait狀态保持一段時間。這個要求的提出是由于消息在網絡中傳輸時可能延遲。如果在連接配接兩端都完成了關閉握手後,它們都移除了其底層資料結 構,而此時在同樣一對套接字位址之間又立即建立了新的連接配接,那麼前一個連接配接在網絡上傳輸時延遲的消息就可能在新連接配接建立後到達。由于其包含了相同的源位址 和目的位址,舊消息就會被錯誤地認為是屬于新連接配接的,其包含的資料就可能被錯誤地配置設定到應用程式中。

   雖然這種情形可能很少發生,TCP還是使用了包括Time-Wait狀态在内的多種機制對其進行防範。Time-Wait狀态用于保證每個TCP連接配接都 在一段平靜時間内結束,這期間不會有資料發送。平靜時間的長度應該等于分組封包在網絡上存留的最長時間的兩倍。是以,當一個連接配接完全結束(即套接字資料結 構離開Time-Wait狀态并被删除),并為同樣一對位址上的新連接配接清理道路後,就不會再有舊執行個體發送的消息還存留在網絡中。實際上,平靜時間的長度要 依賴于具體實作,因為沒有機制能真正限制分組封包在網絡上能夠延遲的時間。通常使用的時間範圍是4分鐘減到30秒,或更短。

  Time-Wait狀态最重要的作用是,隻要底層套接字資料結構還存在,就不允許在相同的本地端口上關聯其他套接字。尤其是試圖使用該端口建立新的Socket執行個體時,将抛出IOException異常。

TCP三向交握/四次揮手詳解

1、建立連接配接協定(三次握手)

(1)用戶端發送一個帶SYN标志的TCP封包到伺服器。這是三次握手過程中的封包1。

(2) 伺服器端回應用戶端的,這是三次握手中的第2個封包,這個封包同時帶ACK标志和SYN标志。是以它表示對剛才用戶端SYN封包的回應;同時又标志SYN給用戶端,詢問用戶端是否準備好進行資料通訊。

(3) 客戶必須再次回應服務段一個ACK封包,這是封包段3。

2、連接配接終止協定(四次揮手)

   由于TCP連接配接是全雙工的,是以每個方向都必須單獨進行關閉。這原則是當一方完成它的資料發送任務後就能發送一個FIN來終止這個方向的連接配接。收到一個 FIN隻意味着這一方向上沒有資料流動,一個TCP連接配接在收到一個FIN後仍能發送資料。首先進行關閉的一方将執行主動關閉,而另一方執行被動關閉。

 (1) TCP用戶端發送一個FIN,用來關閉客戶到伺服器的資料傳送(封包段4)。

 (2) 伺服器收到這個FIN,它發回一個ACK,确認序号為收到的序号加1(封包段5)。和SYN一樣,一個FIN将占用一個序号。

 (3) 伺服器關閉用戶端的連接配接,發送一個FIN給用戶端(封包段6)。

 (4) 客戶段發回ACK封包确認,并将确認序号設定為收到序号加1(封包段7)。

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可用狀态了。

最後有2個問題的回答,我自己分析後的結論(不一定保證100%正确):

1、 為什麼建立連接配接協定是三次握手,而關閉連接配接卻是四次握手呢?

這 是因為服務端的LISTEN狀态下的SOCKET當收到SYN封包的建連請求後,它可以把ACK和SYN(ACK起應答作用,而SYN起同步作用)放在一 個封包裡來發送。但關閉連接配接時,當收到對方的FIN封包通知時,它僅僅表示對方沒有資料發送給你了;但未必你所有的資料都全部發送給對方了,是以你可以未 必會馬上會關閉SOCKET,也即你可能還需要發送一些資料給對方之後,再發送FIN封包給對方來表示你同意現在可以關閉連接配接了,是以它這裡的ACK封包 和FIN封包多數情況下都是分開發送的。

2、 為什麼TIME_WAIT狀态還需要等2MSL後才能傳回到CLOSED狀态?

這 是因為:雖然雙方都同意關閉連接配接了,而且握手的4個封包也都協調和發送完畢,按理可以直接回到CLOSED狀态(就好比從SYN_SEND狀态到 ESTABLISH狀态那樣);但是因為我們必須要假想網絡是不可靠的,你無法保證你最後發送的ACK封包會一定被對方收到,是以對方處于 LAST_ACK狀态下的SOCKET可能會因為逾時未收到ACK封包,而重發FIN封包,是以這個TIME_WAIT狀态的作用就是用來重發可能丢失的 ACK封包。