CLOSE WAIT狀態的生成原因

2021-04-15 15:18:34 字數 4003 閱讀 2863

關閉socket分為主動關閉(active closure)和被動關閉(passive closure)兩種情況。前者是指有本地主機主動發起的關閉;而後者則是指本地主機檢測到遠端主機發起關閉之後,作出回應,從而關閉整個連線。

其狀態圖如下圖所示:

起初每個socket都是closed狀態,當客戶端初使化乙個連線,他傳送乙個syn包到伺服器,客戶端進入syn_sent狀態。

伺服器接收到syn包,反饋乙個syn-ack包,客戶端接收後返饋乙個ack包客戶端變成established狀態,如果長時間沒收到syn-ack包,客戶端超時進入closed狀態。

當伺服器繫結並監聽某一埠時,socket的狀態是listen,當客戶企圖建立連線時,伺服器收到乙個syn包,並反饋syn-ack包。伺服器狀態變成syn_rcvd,當客戶端傳送乙個ack包時,伺服器socket變成established狀態。

當乙個程式在established狀態時有兩種圖徑關閉它, 第一是主動關閉,第二是被動關閉。如果你要主動關閉的話,傳送乙個fin包。當你的程式closesocket或者shutdown(標記),你的程式傳送乙個fin包到peer,你的socket變成fin_wait_1狀態。peer反饋乙個ack包,你的socket進入fin_wait_2狀態。如果peer也在關閉連線,那麼它將傳送乙個fin包到你的電腦,你反饋乙個ack包,並轉成time_wait狀態。

time_wait狀態又號2msl等待狀態。msl意思是最大段生命週期(maximum segment lifetime)表明乙個包存在於網路上到被丟棄之間的時間。每個ip包有乙個ttl(time_to_live),當它減到0時則包被丟棄。每個路由器使ttl減一並且傳送該包。當乙個程式進入time_wait狀態時,他有2個msl的時間,這個充許tcp重發最後的ack,萬一最後的ack丟失了,使得fin被重新傳輸。在2msl等待狀態完成後,socket進入closed狀態。

被動關閉:當程式收到乙個fin包從peer,並反饋乙個ack包,於是程式的socket轉入close_wait狀態。因為peer已經關閉了,所以不能發任何訊息了。但程式還可以。要關閉連線,程式自已傳送給自已fin,使程式的tcp socket狀態變成last_ack狀態,當程式從peer收到ack包時,程式進入closed狀態。

root許可權執行 lsof -n httpd | grep tcp ,可以檢視是否有大量close_wait

摘要:本文闡述了為何socket連線鎖定在close_wait狀態,以及通過什麼措施力求避免這種情況。

不久前,我的socket client程式遇到了乙個非常尷尬的錯誤。它本來應該在乙個socket長連線上持續不斷地向伺服器傳送資料,如果socket連線斷開,那麼程式會自動不斷地重試建立連線。

有一天發現程式在不斷嘗試建立連線,但是總是失敗。用netstat檢視,這個程式竟然有上千個socket連線處於close_wait狀態,以至於達到了上限,所以無法建立新的socket連線了。

為什麼會這樣呢?

它們為什麼會都處在close_wait狀態呢?

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));

也許我們無法避免被凍結在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狀態的生成原因

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 ack client...

CLOSE WAIT狀態的生成原因

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