CLOSE WAIT狀態的生成原因

2021-05-27 18:01:17 字數 3937 閱讀 3379

close_wait狀態的生成原因

首先我們知道,如果我們的client程式處於close_wait狀態的話,說明套接字是被動關閉的!

因為如果是server端主動斷掉當前連線的話,那麼雙方關閉這個tcp連線共需要四個packet:

server  --->  fin  --->  client

server  <---  ack  <---  client

這時候server端處於fin_wait_2狀態;而我們的程式處於close_wait狀態。

server  <---  fin  <---  client

這時client傳送fin給server,client就置為last_ack狀態。

server  --->  ack  --->  client

server回應了ack,那麼client的套接字才會真正置為closed狀態。

我們的程式處於close_wait狀態,而不是last_ack狀態,說明還沒有發fin給server,那麼可能是在關閉連線之前還有許多資料要傳送或者其他事要做,導致沒有發這個fin packet。

原因知道了,那麼為什麼不發fin包呢,難道會在關閉己方連線前有那麼多事情要做嗎?

elssann舉例說,當對方呼叫closesocket的時候,我的程式正在呼叫recv中,這時候有可能對方傳送的fin包我沒有收到,而是由tcp代回了乙個ack包,所以我這邊套接字進入close_wait狀態。

所以他建議在這裡判斷recv函式的返回值是否已出錯,是的話就主動closesocket,這樣防止沒有接收到fin包。

因為前面我們已經設定了recv超時時間為30秒,那麼如果真的是超時了,這裡收到的錯誤應該是wsaetimedout,這種情況下也可以主動關閉連線的。

還有乙個問題,為什麼有數千個連線都處於這個狀態呢?難道那段時間內,伺服器端總是主動拆除我們的連線嗎?

不管怎麼樣,我們必須防止類似情況再度發生!

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

重用本地位址和埠

以前我總是乙個埠不行,就換乙個新的使用,所以導致讓數千個埠進入close_wait狀態。如果下次還發生這種尷尬狀況,我希望加乙個限定,只是當前這個埠處於close_wait狀態!

在呼叫sockconnected = socket(af_inet, sock_stream, 0);

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

/// 允許重用本地位址和埠:

/// 這樣的好處是,即使socket斷了,呼叫前面的socket函式也不會占用另乙個,而是始終就是乙個埠

/// 這樣防止socket始終連線不上,那麼按照原來的做法,會不斷地換埠。

int nreuseaddr = 1;

setsockopt(sockconnected,

sol_socket,

so_reuseaddr,

(const char*)&nreuseaddr,

sizeof(int));

教科書上是這麼說的:這樣,假如伺服器關閉或者退出,造成本地位址和埠都處於time_wait狀態,那麼so_reuseaddr就顯得非常有用。

也許我們無法避免被凍結在close_wait狀態永遠不出現,但起碼可以保證不會占用新的埠。

其次,我們要設定so_linger套接字選項:

從容關閉還是強行關閉?

linger是「拖延」的意思。

預設情況下(win2k),so_dontlinger套接字選項的是1;so_linger選項是,linger為。

如果在傳送資料的過程中(send()沒有完成,還有資料沒傳送)而呼叫了closesocket(),以前我們一般採取的措施是「從容關閉」:

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

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

shutdown(sockconnected, sd_both);

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

closesocket(sockconnected);

我們這次要這麼做:

設定so_linger為零(亦即linger結構中的l_onoff域設為非零,但l_linger為0),便不用擔心closesocket調 用進入「鎖定」狀態(等待完成),不論是否有排隊資料未傳送或未被確認。這種關閉方式稱為「強行關閉」,因為套接字的虛電路立即被復位,尚未發出的所有數 據都會丟失。在遠端的recv()呼叫都會失敗,並返回wsaeconnreset錯誤。

在connect成功建立連線之後設定該選項:

linger m_slinger;

m_slinger.l_onoff = 1;  // (在closesocket()呼叫,但是還有資料沒傳送完畢的時候容許逗留)

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

setsockopt(sockconnected,

sol_socket,

so_linger,

(const char*)&m_slinger,

sizeof(linger));

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

我的意思是:當一方關閉連線後,另外一方沒有檢測到,就導致了close_wait的出現,上次我的乙個朋友也是這樣,他寫了乙個客戶端和 apache連線,當apache把連線斷掉後,他沒檢測到,出現了close_wait,後來我叫他檢測了這個地方,他新增了呼叫 closesocket的**後,這個問題就消除了。 

如果你在關閉連線前還是出現close_wait,建議你取消shutdown的呼叫,直接兩邊closesocket試試。

另外乙個問題:

比如這樣的乙個例子: 

當客戶端登入上伺服器後,傳送身份驗證的請求,伺服器收到了資料,對客戶端身份進行驗證,發現密碼錯誤,這時候伺服器的一般做法應該是先傳送乙個密碼錯誤的資訊給客戶端,然後把連線斷掉。

如果把 

m_slinger.l_onoff = 1; 

m_slinger.l_linger = 0; 

這樣設定後,很多情況下,客戶端根本就收不到密碼錯誤的訊息,連線就被斷了。

出現close_wait的原因很簡單,就是某一方在網路連線斷開後,沒有檢測到這個錯誤,沒有執行closesocket,導致了這個狀態的實現,這在tcp/ip協議的狀態變遷圖上可以清楚看到。同時和這個相對應的還有一種叫time_wait的。

另外,把socket的so_linger設定為0秒拖延(也就是立即關閉)在很多時候是有害處的。 

還有,把埠設定為可復用是一種不安全的網路程式設計方法。

再看這個圖:

斷開連線的時候, 

當發起主動關閉的左邊這方傳送乙個fin過去後,右邊被動關閉的這方要回應乙個ack,這個ack是tcp回應的,而不 是應用程式傳送的,此時,被動關閉的一方就處於close_wait狀態了。如果此時被動關閉的這一方不再繼續呼叫closesocket,那麼他就不會 傳送接下來的fin,導致自己老是處於close_wait。只有被動關閉的這一方呼叫了closesocket,才會傳送乙個fin給主動關閉的這一 方,同時也使得自己的狀態變遷為last_ack。

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

當對方呼叫closesocket的時候,你的程式正在

int nret = recv(s,....); 

if (nret == socket_error) 

很多人就是忘記了那句closesocket,這種**太常見了。

我的理解,當主動關閉的一方傳送fin到被動關閉這邊後,被動關閉這邊的tcp馬上回應乙個ack過去,同時向上面應用程式提交乙個error,導 致上面的socket的send或者recv返回socket_error,正常情況下,如果上面在返回socket_error後呼叫了 closesocket,那麼被動關閉的者一方的tcp就會傳送乙個fin過去,自己的狀態就變遷到last_ack.

CLOSE WAIT狀態的生成原因

關閉socket分為主動關閉 active closure 和被動關閉 passive closure 兩種情況。前者是指有本地主機主動發起的關閉 而後者則是指本地主機檢測到遠端主機發起關閉之後,作出回應,從而關閉整個連線。其狀態圖如下圖所示 起初每個socket都是closed狀態,當客戶端初使化...

CLOSE WAIT狀態的生成原因

close wait狀態的生成原因 首先我們知道,如果我們的client程式處於close wait狀態的話,說明套接字是被動關閉的!因為如果是server端主動斷掉當前連線的話,那麼雙方關閉這個tcp連線共需要四個packet server fin client server ack client...

CLOSE WAIT狀態的生成原因

close wait狀態的生成原因 首先我們知道,如果我們的client程式處於close wait狀態的話,說明套接字是被動關閉的!因為如果是server端主動斷掉當前連線的話,那麼雙方關閉這個tcp連線共需要四個packet server fin client server 這時候server端...