1. 程式人生 > >TCP/IP詳解--連線狀態變遷圖CLOSE WAIT

TCP/IP詳解--連線狀態變遷圖CLOSE WAIT

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

                 終止一個連線要經過4次握手。這由TCP的半關閉(half-close)造成的。既然一個TCP連線是全雙工(即資料在兩個方向上能同時傳遞,可理解為兩個方向相反的獨立通道),因此每個方向必須單獨地進行關閉。這原則就是當一方完成它的資料傳送任務後就能傳送一個FIN來終止這個方向連線。當一端收到一個FIN
核心讓read返回0 通知應用層另一端已經終止了向本端的資料傳送。傳送FIN通常是應用層對socket進行 關閉的結果。

例如:TCP客戶端傳送一個FIN,用來關閉從客戶到伺服器的資料傳送。


    半關閉對伺服器究竟有什麼影響呢?先看看下面的TCP狀態轉化圖

                                  tcp狀態裝換圖

首先對上面這個圖示進行解釋:

CLOSED:表示初始狀態。對服務端和C客戶端雙方都一樣。
        LISTEN:表示監聽狀態。服務端呼叫了listen函式,可以開始accept連線了。
        SYN_SENT:表示客戶端已經發送了SYN報文。當客戶端呼叫connect函式發起連線時,首先發SYN給服務端,然後自己進入SYN_SENT狀態,並等待服務端傳送ACK+SYN。
        SYN_RCVD:表示服務端收到客戶端傳送SYN報文。服務端收到這個報文後,進入SYN_RCVD狀態,然後傳送ACK+SYN給客戶端。
        ESTABLISHED:表示連線已經建立成功了。服務端傳送完ACK+SYN後進入該狀態,客戶端收到ACK後也進入該狀態。
        FIN_WAIT_1:表示主動關閉連線。無論哪方呼叫close函式傳送FIN報文都會進入這個這個狀態。
        FIN_WAIT_2:表示被動關閉方同意關閉連線。主動關閉連線方收到被動關閉方返回的ACK後,會進入該狀態。
        TIME_WAIT:表示收到對方的FIN報文併發送了ACK報文,就等2MSL後即可回到CLOSED狀態了。如果FIN_WAIT_1狀態下,收到對方同時帶FIN標誌和ACK標誌的報文時,可以直接進入TIME_WAIT狀態,而無須經過FIN_WAIT_2狀態。
        CLOSING:表示雙方同時關閉連線。如果雙方几乎同時呼叫close函式,那麼會出現雙方同時傳送FIN報文的情況,此時就會出現CLOSING狀態,表示雙方都在關閉連線。
        CLOSE_WAIT:表示被動關閉方等待關閉。當收到對方呼叫close函式傳送的FIN報文時,迴應對方ACK報文,此時進入CLOSE_WAIT狀態。
        LAST_ACK:表示被動關閉方傳送FIN報文後,等待對方的ACK報文狀態,當收到ACK後進入CLOSED狀態。

  

三次握手和四次握手所對應的上述狀態:



客戶端主動關閉時,發出FIN包,收到伺服器的ACK,客戶端停留在FIN_WAIT2狀態。而服務端收到FIN,發出ACK後,停留在COLSE_WAIT狀態。
    這個CLOSE_WAIT狀態非常討厭,它持續的時間非常長,伺服器端如果積攢大量的COLSE_WAIT狀態的socket,有可能將伺服器資源耗盡,進而無法提供服務
    那麼,伺服器上是怎麼產生大量的失去控制的COLSE_WAIT狀態的socket呢?我們來追蹤一下。
    一個很淺顯的原因是,伺服器沒有繼續
發FIN包給客戶端。
    伺服器為什麼不發FIN,可能是業務實現上的需要,現在不是傳送FIN的時機,因為伺服器還有資料要發往客戶端,傳送完了自然就要通過系統呼叫發FIN了,這個場景並不是上面我們提到的持續的COLSE_WAIT狀態,這個在受控範圍之內。
    那麼究竟是什麼原因呢,咱們引入兩個系統呼叫close(sockfd)和shutdown(sockfd,how)接著往下分析。
    在這兒,需要明確的一個概念---- 一個程序開啟一個socket,然後此程序再派生子程序的時候,此socket的sockfd會被繼承。socket是系統級的物件,現在的結果是,此socket被兩個程序開啟,此socket的引用計數會變成2。

    繼續說上述兩個系統呼叫對socket的關閉情況。
    呼叫close(sockfd)時,核心檢查此fd對應的socket上的引用計數。如果引用計數大於1,那麼將這個引用計數減1,然後返回。如果引用計數等於1,那麼核心會真正通過發FIN來關閉TCP連線。
    呼叫shutdown(sockfd,SHUT_RDWR)時,核心不會檢查此fd對應的socket上的引用計數,直接通過發FIN來關閉TCP連線。

     現在應該真相大白了,可能是伺服器的實現有點問題,父程序打開了socket,然後用派生子程序來處理業務,父程序繼續對網路請求進行監聽,永遠不會終止。客戶端發FIN過來的時候,處理業務的子程序的read返回0,子程序發現對端已經關閉了,直接呼叫close()對本端進行關閉。實際上,僅僅使socket的引用計數減1,socket並沒關閉。從而導致系統中又多了一個CLOSE_WAIT的socket。。。

如何避免這樣的情況發生?
子程序的關閉處理應該是這樣的:
shutdown(sockfd,SHUT_RDWR);

close(sockfd);
這樣處理,伺服器的FIN會被髮出,socket進入LAST_ACK狀態,等待最後的ACK到來,就能進入初始狀態CLOSED。


補充一下shutdown()的函式說明

linux系統下使用shutdown系統呼叫來控制socket的關閉方式

int shutdown(intsockfd,int how);

引數how允許為shutdown操作選擇以下幾種方式:

SHUT_RD:關閉連線的讀端。也就是該套接字不再接受資料,任何當前在套接字接受緩衝區的資料將被丟棄。程序將不能對該套接字發出任何讀操作。對TCP套接字該呼叫之後接受到的任何資料將被確認然後被丟棄。

SHUT_WR:關閉連線的寫端。

SHUT_RDWR:相當於呼叫shutdown兩次:首先是以SHUT_RD,然後以SHUT_WR

注意:

在多程序中如果一個程序中shutdown(sfd, SHUT_RDWR)後其它的程序將無法進行通訊.如果一個程序close(sfd)將不會影響到其它程序.

           

給我老師的人工智慧教程打call!http://blog.csdn.net/jiangjunshow

這裡寫圖片描述