• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            yehao's Blog

            解決TCP連接數過多的問題

            TCP狀態遷移,CLOSE_WAIT & FIN_WAIT2 的問題

            TCP狀態遷移
            大家對netstat -a命令很熟悉,但是,你有沒有注意到STATE一欄呢,基本上顯示著established,time_wait,close_wait等,這些到底是 什么意思呢,在這篇文章,我將會詳細的闡述。
            大家很明白TCP初始化連接三次握手吧:發SYN包,然后返回SYN/ACK包,再發ACK包,連接正式建立。但是這里有點出入,當請求者收到SYS /ACK包后,就開始建立連接了,而被請求者第三次握手結束后才建立連接。但是大家明白關閉連接的工作原理嗎?關閉連接要四次握手:發FIN包,ACK 包,FIN包,ACK包,四次握手!!為什么呢,因為TCP連接是全雙工,我關了你的連接,并不等于你關了我的連接。

            客戶端TCP狀態遷移:
            CLOSED->SYN_SENT->ESTABLISHED->FIN_WAIT_1->FIN_WAIT_2->TIME_WAIT->CLOSED
            服務器TCP狀態遷移:
            CLOSED->LISTEN->SYN收到 ->ESTABLISHED->CLOSE_WAIT->LAST_ACK->CLOSED

            當客戶端開始連接時,服務器還處于LISTENING,
            客戶端發一個SYN包后,他就處于SYN_SENT狀態,服務器就處于SYS收到狀態,
            然后互相確認進入連接狀態ESTABLISHED.


            當客戶端請求關閉連接時,客戶端發送一個FIN包后,客戶端就進入FIN_WAIT_1狀態,等待對方的確認包,
            服務器發送一個ACK包給客戶,客戶端收到ACK包后結束FIN_WAIT_1狀態,進入FIN_WAIT_2狀態,等待服務器發過來的關閉請求,
            服務器發一個FIN包后,進入CLOSE_WAIT狀態,
            當客戶端收到服務器的FIN包,FIN_WAIT_2狀態就結束,然后給服務器端的FIN包給以一個確認包,客戶端這時進入TIME_WAIT,
            當服務器收到確認包后,CLOSE_WAIT狀態結束了,
            這時候服務器端真正的關閉了連接.但是客戶端還在TIME_WAIT狀態下,

            什么時候結束呢.我在這里再講到一個新名詞:2MSL等待狀態,其實TIME_WAIT就是2MSL等待狀態,
            為什么要設置這個狀態,原因是有足夠的時間讓ACK包到達服務器端,如果服務器端沒收到ACK包,超時了,然后重新發一個FIN包,直到服務器收到ACK 包.

            TIME_WAIT狀態等待時間是在TCP重新啟動后不連接任何請求的兩倍.
            大家有沒有發現一個問題:如果對方在第三次握手的時候出問題,如發FIN包的時候,不知道什么原因丟了這個包,然而這邊一直處在FIN_WAIT_2狀 態,而且TCP/IP并沒有設置這個狀態的過期時間,那他一直會保留這個狀態下去,越來越多的FIN_WAIT_2狀態會導致系統崩潰.

            上面我碰到的這個問題主要因為TCP的結束流程未走完,造成連接未釋放。現設客戶端主動斷開連接,流程如下

             

            如上圖所示,

            Client                            消息                                    Server

                     close()
            ------ FIN ------->
            FIN_WAIT1                                                         CLOSE_WAIT
            <----- ACK -------
            FIN_WAIT2
            close()
            <------ FIN ------                    
            TIME_WAIT                                                       LAST_ACK      

                                                  ------ ACK ------->
            CLOSED
            CLOSED


            由于Server的Socket在客戶端已經關閉時而沒有調用關閉,
            造成服務器端的連接處在“掛起”狀態,而客戶端則處在等待應答的狀態上。
            此問題的典型特征是:
            一端處于FIN_WAIT2 ,而另一端處于CLOSE_WAIT.

            不過,根本問題還是程序寫的不好,有待提高

            -------------------------------------------------------------------------

            CLOSE_WAIT,TCP的癌癥,TCP的朋友。

            CLOSE_WAIT狀態的生成原因
            首先我們知道,如果我們的服務器程序APACHE處于CLOSE_WAIT狀態的話,說明套接字是被動關閉的!

            因為如果是CLIENT端主動斷掉當前連接的話,那么雙方關閉這個TCP連接共需要四個packet:

            Client ---> FIN ---> Server

            Client <--- ACK <--- Server

            這時候Client端處于FIN_WAIT_2狀態;而Server 程序處于CLOSE_WAIT狀態。

            Client <--- FIN <--- Server

            這時Server 發送FIN給Client,Server 就置為LAST_ACK狀態。

            Client ---> ACK ---> Server

            Client回應了ACK,那么Server 的套接字才會真正置為CLOSED狀態。

            Server 程序處于CLOSE_WAIT狀態,而不是LAST_ACK狀態,說明還沒有發FIN給Client,那么可能是在關閉連接之前還有許多數據要發送或者其 他事要做,導致沒有發這個FIN packet。

            通常來說,一個CLOSE_WAIT會維持至少2個小時的時間。如果有個流氓特地寫了個程序,給你造成一堆的 CLOSE_WAIT,消耗你的資源,那么通常是等不到釋放那一刻,系統就已經解決崩潰了。

            只能通過修改一下TCP/IP的參數,來縮短這個時間:修改tcp_keepalive_*系列參數有助于解決這個 問題。

            解決這個問題的方法是修改系統的參數,系統默認超時時間的是7200秒,也就是2小時, 這個太大了,可以修改如下幾個參數:

            sysctl -w net.ipv4.tcp_keepalive_time=30
            sysctl -w net.ipv4.tcp_keepalive_probes=2
            sysctl -w net.ipv4.tcp_keepalive_intvl=2

            然后,執行sysctl命令使修改生效。

             

            連接進程是通過一系列狀態表示的,這些狀態有:
            LISTEN,SYN-SENT,SYN-RECEIVED,ESTABLISHED,FIN-WAIT-1,FIN-WAIT-2,CLOSE- WAIT,CLOSING,LAST-ACK,TIME-WAIT和CLOSED。

            各個狀態的意義如下:
            LISTEN - 偵聽來自遠方TCP端口的連接請求;
            SYN-SENT -在發送連接請求后等待匹配的連接請求;
            SYN-RECEIVED - 在收到和發送一個連接請求后等待對連接請求的確認;
            ESTABLISHED- 代表一個打開的連接,數據可以傳送給用戶;
            FIN-WAIT-1 - 等待遠程TCP的連接中斷請求,或先前的連接中斷請求的確認;
            FIN-WAIT-2 - 從遠程TCP等待連接中斷請求;
            CLOSE-WAIT - 等待從本地用戶發來的連接中斷請求;
            CLOSING -等待遠程TCP對連接中斷的確認;
            LAST-ACK - 等待原來發向遠程TCP的連接中斷請求的確認;
            TIME-WAIT -等待足夠的時間以確保遠程TCP接收到連接中斷請求的確認;
            CLOSED - 沒有任何連接狀態;

            TCP連接過程是狀態的轉換,促使發生狀態轉換的是用戶調用:
            OPEN,SEND,RECEIVE,CLOSE,ABORT和STATUS;
            傳送過來的數據段,特別那些包括以下標記的數據段SYN,ACK,RST和FIN;
            還有超時,上面所說的都會時TCP狀態發生變化。

             

             

            TCP連接的狀態轉換圖

             

            n多人都 知道,排除和定 位網絡或系統故障大有幫助,但是怎牢牢地這張圖刻在中呢?那你就一定要對 這張圖一個狀態,及轉換的過程有深刻地認識,不能只停留在一知半解之中。下面對這張圖的11種狀 態詳細解釋一下,以便加強記憶!不過在這之前,先回顧一下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_1FIN_WAIT_2狀態的真正含義都是表示等待對方的FIN報 文。而這兩種狀態的區別是:FIN_WAIT_1狀態實際上是當SOCKETESTABLISHED狀態時,它想主動關閉連接,向對方發送了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報文的建連請求后,它可以把ACKSYNACK起 應答作用,而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報 文,并保證于此。

             

             

             


             

                    斷開連接的時候, 當發起主動關閉的左邊這方發送一個FIN過去后,
            右邊被動關閉的這方要回應一個ACK,這個ACK是TCP回應的,而不是應用程序發送的,
            此時,被動關閉的一方就處于CLOSE_WAIT狀態了。


            如果此時被動關閉的這一方不再繼續調用closesocket,那么他就不會發送接下來的FIN,導致自己老是處于CLOSE_WAIT。
            只有被動關閉的這一方調用了 closesocket,才會發送一個FIN給主動關閉的這一方,同時也使得自己的狀態變遷為LAST_ACK。

            比如被動關閉的是客戶端.

            當對方調用closesocket的時候,你的程序正在

            int nRet = recv(s,....);
            if (nRet == SOCKET_ERROR)
            {
            // closesocket(s);
            return FALSE;
            }

            很多人就是忘記了那句closesocket,這種代碼太常見了。
            我的理解,
            當主動關閉的一方發送FIN到被動關閉這邊后,被動關閉這邊的TCP馬上回應一個ACK過去,同時向上面應用程序提交一個ERROR,
            導致上面的SOCKET的send或者recv返回SOCKET_ERROR.

            正常情況下,如果上面在返回SOCKET_ERROR后調用了closesocket, 那么被動關閉的者一方的TCP就會發送一個FIN過去,自己的狀態就變遷到LAST_ACK.

             


            服務器上出現大量的close_wait的例子和解決方法(例子從網上找的,基本差不多)

            oracle9i@RHEL3oracle9i]$ /usr/sbin/lsof -i | grep 6800
            oracle    22725 oracle9i    3u IPv4 18621468       TCP RHEL3:6800 (LISTEN)
            oracle    22725 oracle9i    4u IPv4 18621469       TCP RHEL3:6800->RHEL3:2174 (CLOSE_WAIT)
            oracle    22725 oracle9i    8u IPv4 18621568       TCP RHEL3:6800->RHEL3:2175 (CLOSE_WAIT)
            oracle    22725 oracle9i    9u IPv4 18621578       TCP RHEL3:6800->RHEL3:2176 (CLOSE_WAIT)
            oracle    22726 oracle9i    3u IPv4 18621468       TCP RHEL3:6800 (LISTEN)
            oracle    22726 oracle9i    4u IPv4 18621469       TCP RHEL3:6800->RHEL3:2174 (CLOSE_WAIT)
            oracle    22726 oracle9i    8u IPv4 18621568       TCP RHEL3:6800->RHEL3:2175 (CLOSE_WAIT)
            oracle    22726 oracle9i    9u IPv4 18621578       TCP RHEL3:6800->RHEL3:2176 (CLOSE_WAIT)

            [oracle9i@RHEL3 oracle9i]$ kill -9 22725

            # 22725, 22726就是使用該6800端口的進程號(PID)。
            [oracle9i@RHEL3 oracle9i]$ /usr/sbin/lsof -i | grep 6800

            進程被kill時,會釋放占用的所有鏈接句柄。

            該問題的出現原因網上到處都是,也就是Socket的Client端出現異常沒有Close就退出了。
            //上面這句話不太準確,應該是被動關閉連接一端沒有closesocket就退出了,此時被動關閉一端就處于close_wait 狀態

            posted on 2011-05-23 17:32 厚積薄發 閱讀(10720) 評論(0)  編輯 收藏 引用 所屬分類: 網絡編程

            導航

            <2025年5月>
            27282930123
            45678910
            11121314151617
            18192021222324
            25262728293031
            1234567

            統計

            常用鏈接

            留言簿

            隨筆分類

            文章分類

            文章檔案

            搜索

            最新評論

            久久久久高潮综合影院| 久久成人18免费网站| 婷婷久久精品国产| 久久久国产精品福利免费 | 久久久久久噜噜精品免费直播| 97精品依人久久久大香线蕉97 | 久久亚洲精品国产精品婷婷| 国产精品99久久久久久猫咪| 日本三级久久网| 久久九九久精品国产免费直播| 久久夜色精品国产亚洲| 国产精品伊人久久伊人电影 | 久久香蕉超碰97国产精品| 精品国产99久久久久久麻豆| 久久中文字幕人妻丝袜| 综合人妻久久一区二区精品| 亚洲色大成网站WWW久久九九| 日本久久久久亚洲中字幕| 狠狠色丁香久久综合五月| Xx性欧美肥妇精品久久久久久| 99热都是精品久久久久久| 亚洲国产天堂久久久久久| 国内精品久久久久影院薰衣草| 久久精品人人做人人爽电影蜜月| 国产精品久久精品| 久久精品夜色噜噜亚洲A∨| 久久这里的只有是精品23| 久久夜色精品国产网站| 国产农村妇女毛片精品久久| 无码精品久久一区二区三区| 亚洲AV无码久久寂寞少妇| 国产精品伦理久久久久久| 亚洲精品无码久久久久去q| 久久精品成人免费网站| 亚洲国产成人精品91久久久 | 日本免费久久久久久久网站| 理论片午午伦夜理片久久 | 久久婷婷五月综合成人D啪| 久久国产精品免费一区二区三区| 区亚洲欧美一级久久精品亚洲精品成人网久久久久 | 伊人热热久久原色播放www|