• <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>

            Prayer

            在一般中尋求卓越
            posts - 1256, comments - 190, trackbacks - 0, articles - 0
              C++博客 :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

            CLOSE_WAIT狀態(tài)的生成原因[轉]

            Posted on 2009-04-01 19:29 Prayer 閱讀(809) 評論(0)  編輯 收藏 引用 所屬分類: SOCKET

            CLOSE_WAIT狀態(tài)的生成原因
            首先我們知道,如果我們的Client程序處于CLOSE_WAIT狀態(tài)的話,說明套接字是被動關閉的!

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

                   Server  --->  FIN  --->  Client

                   Server  <---  ACK  <---  Client

                這時候Server端處于FIN_WAIT_2狀態(tài);而我們的程序處于CLOSE_WAIT狀態(tài)。

                   Server  <---  FIN  <---  Client

            這時Client發(fā)送FIN給Server,Client就置為LAST_ACK狀態(tài)。

                    Server  --->  ACK  --->  Client

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

             

            我們的程序處于CLOSE_WAIT狀態(tài),而不是LAST_ACK狀態(tài),說明還沒有發(fā)FIN給Server,那么可能是在關閉連接之前還有許多數(shù)據(jù)要發(fā)送或者其他事要做,導致沒有發(fā)這個FIN packet。

             

            原因知道了,那么為什么不發(fā)FIN包呢,難道會在關閉己方連接前有那么多事情要做嗎?

            elssann舉例說,當對方調用closesocket的時候,我的程序正在調用recv中,這時候有可能對方發(fā)送的FIN包我沒有收到,而是由TCP代回了一個ACK包,所以我這邊套接字進入CLOSE_WAIT狀態(tài)。

            所以他建議在這里判斷recv函數(shù)的返回值是否已出錯,是的話就主動closesocket,這樣防止沒有接收到FIN包。

            因為前面我們已經(jīng)設置了recv超時時間為30秒,那么如果真的是超時了,這里收到的錯誤應該是WSAETIMEDOUT,這種情況下也可以主動關閉連接的。

             

            還有一個問題,為什么有數(shù)千個連接都處于這個狀態(tài)呢?難道那段時間內,服務器端總是主動拆除我們的連接嗎?

             

            不管怎么樣,我們必須防止類似情況再度發(fā)生!

            首先,我們要保證原來的端口可以被重用,這可以通過設置SO_REUSEADDR套接字選項做到:


            重用本地地址和端口
            以前我總是一個端口不行,就換一個新的使用,所以導致讓數(shù)千個端口進入CLOSE_WAIT狀態(tài)。如果下次還發(fā)生這種尷尬狀況,我希望加一個限定,只是當前這個端口處于CLOSE_WAIT狀態(tài)!

            在調用

            sockConnected = socket(AF_INET, SOCK_STREAM, 0);

            之后,我們要設置該套接字的選項來重用:

            /// 允許重用本地地址和端口:

            /// 這樣的好處是,即使socket斷了,調用前面的socket函數(shù)也不會占用另一個,而是始終就是一個端口

            /// 這樣防止socket始終連接不上,那么按照原來的做法,會不斷地換端口。

            int nREUSEADDR = 1;

            setsockopt(sockConnected,

                          SOL_SOCKET,

                          SO_REUSEADDR,

                          (const char*)&nREUSEADDR,

                          sizeof(int));
             

            教科書上是這么說的:這樣,假如服務器關閉或者退出,造成本地地址和端口都處于TIME_WAIT狀態(tài),那么SO_REUSEADDR就顯得非常有用。

            也許我們無法避免被凍結在CLOSE_WAIT狀態(tài)永遠不出現(xiàn),但起碼可以保證不會占用新的端口。

            其次,我們要設置SO_LINGER套接字選項:

            從容關閉還是強行關閉?
            LINGER是“拖延”的意思。

            默認情況下(Win2k),SO_DONTLINGER套接字選項的是1;SO_LINGER選項是,linger為{l_onoff:0,l_linger:0}。

            如果在發(fā)送數(shù)據(jù)的過程中(send()沒有完成,還有數(shù)據(jù)沒發(fā)送)而調用了closesocket(),以前我們一般采取的措施是“從容關閉”:

            因為在退出服務或者每次重新建立socket之前,我都會先調用

            /// 先將雙向的通訊關閉

                 shutdown(sockConnected, SD_BOTH);

                 /// 安全起見,每次建立Socket連接前,先把這個舊連接關閉

            closesocket(sockConnected);

             

            我們這次要這么做:

            設置SO_LINGER為零(亦即linger結構中的l_onoff域設為非零,但l_linger為0),便不用擔心closesocket調用進入“鎖定”狀態(tài)(等待完成),不論是否有排隊數(shù)據(jù)未發(fā)送或未被確認。這種關閉方式稱為“強行關閉”,因為套接字的虛電路立即被復位,尚未發(fā)出的所有數(shù)據(jù)都會丟失。在遠端的recv()調用都會失敗,并返回WSAECONNRESET錯誤。

            在connect成功建立連接之后設置該選項:

            linger m_sLinger;

            m_sLinger.l_onoff = 1;  // (在closesocket()調用,但是還有數(shù)據(jù)沒發(fā)送完畢的時候容許逗留)

            m_sLinger.l_linger = 0; // (容許逗留的時間為0秒)

            setsockopt(sockConnected,

                     SOL_SOCKET,

                     SO_LINGER,

                     (const char*)&m_sLinger,

                     sizeof(linger));
             

             

            總結
            也許我們避免不了CLOSE_WAIT狀態(tài)凍結的再次出現(xiàn),但我們會使影響降到最小,希望那個重用套接字選項能夠使得下一次重新建立連接時可以把CLOSE_WAIT狀態(tài)踢掉。


            我的意思是:當一方關閉連接后,另外一方?jīng)]有檢測到,就導致了CLOSE_WAIT的出現(xiàn),上次我的一個朋友也是這樣,他寫了一個客戶端和 APACHE連接,當APACHE把連接斷掉后,他沒檢測到,出現(xiàn)了CLOSE_WAIT,后來我叫他檢測了這個地方,他添加了調用 closesocket的代碼后,這個問題就消除了。
            如果你在關閉連接前還是出現(xiàn)CLOSE_WAIT,建議你取消shutdown的調用,直接兩邊closesocket試試。


            另外一個問題:

            比如這樣的一個例子:
            當客戶端登錄上服務器后,發(fā)送身份驗證的請求,服務器收到了數(shù)據(jù),對客戶端身份進行驗證,發(fā)現(xiàn)密碼錯誤,這時候服務器的一般做法應該是先發(fā)送一個密碼錯誤的信息給客戶端,然后把連接斷掉。

            如果把
            m_sLinger.l_onoff = 1;
            m_sLinger.l_linger = 0;
            這樣設置后,很多情況下,客戶端根本就收不到密碼錯誤的消息,連接就被斷了。

             


            出現(xiàn)CLOSE_WAIT的原因很簡單,就是某一方在網(wǎng)絡連接斷開后,沒有檢測到這個錯誤,沒有執(zhí)行closesocket,導致了這個狀態(tài)的實現(xiàn),這在TCP/IP協(xié)議的狀態(tài)變遷圖上可以清楚看到。同時和這個相對應的還有一種叫TIME_WAIT的。

            另外,把SOCKET的SO_LINGER設置為0秒拖延(也就是立即關閉)在很多時候是有害處的。
            還有,把端口設置為可復用是一種不安全的網(wǎng)絡編程方法。

            久久久精品午夜免费不卡| 欧美久久亚洲精品| 国产精品99精品久久免费| 久久精品aⅴ无码中文字字幕重口 久久精品a亚洲国产v高清不卡 | 久久无码AV中文出轨人妻| 日韩AV无码久久一区二区| 93精91精品国产综合久久香蕉| 美女久久久久久| 91精品国产9l久久久久| 色婷婷久久久SWAG精品| jizzjizz国产精品久久| 亚洲伊人久久成综合人影院 | 久久精品国产99久久久古代| 久久精品水蜜桃av综合天堂| 久久男人AV资源网站| 久久人人爽人人爽人人片AV不 | 99精品久久精品一区二区| 久久精品国产亚洲Aⅴ蜜臀色欲| 久久久久亚洲AV成人网人人软件 | 蜜臀av性久久久久蜜臀aⅴ麻豆| 国产精品99久久久久久宅男| 无码人妻少妇久久中文字幕蜜桃 | 亚洲国产精品久久久久| 色诱久久久久综合网ywww| 久久久亚洲精品蜜桃臀| 久久伊人精品青青草原高清| 久久人爽人人爽人人片AV | 一本伊大人香蕉久久网手机| 久久久久av无码免费网| 思思久久99热只有频精品66| 久久精品18| 欧美粉嫩小泬久久久久久久| 久久se精品一区二区影院 | 久久青青草原精品国产软件| 国产午夜精品久久久久九九电影| 狠狠色丁香婷婷久久综合不卡| 久久国产精品99精品国产| 国产成人久久精品一区二区三区| 久久99亚洲网美利坚合众国| 99久久精品午夜一区二区| 久久精品国产影库免费看|