TCP close_wait 状态的解释
CLOSE_WAIT狀態(tài)的生成原因
首先我們知道,如果我們的Client程序處于CLOSE_WAIT狀態(tài)的話,說明套接字是被動關(guān)閉的!
因為如果是Server端主動斷掉當(dāng)前連接的話,那么雙方關(guān)閉這個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回應(yīng)了ACK,那么Client的套接字才會真正置為CLOSED狀態(tài)。
我們的程序處于CLOSE_WAIT狀態(tài),而不是LAST_ACK狀態(tài),說明還沒有發(fā)FIN給Server,那么可能是在關(guān)閉連接之前還有許多數(shù)據(jù)要發(fā)送或者其他事要做,導(dǎo)致沒有發(fā)這個FIN packet。
原因知道了,那么為什么不發(fā)FIN包呢,難道會在關(guān)閉己方連接前有那么多事情要做嗎?
還有一個問題,為什么有數(shù)千個連接都處于這個狀態(tài)呢?難道那段時間內(nèi),服務(wù)器端總是主動拆除我們的連接嗎?
不管怎么樣,我們必須防止類似情況再度發(fā)生!
首先,我們要防止不斷開辟新的端口,這可以通過設(shè)置SO_REUSEADDR套接字選項做到:
重用本地地址和端口
以前我總是一個端口不行,就換一個新的使用,所以導(dǎo)致讓數(shù)千個端口進(jìn)入CLOSE_WAIT狀態(tài)。如果下次還發(fā)生這種尷尬狀況,我希望加一個限定,只是當(dāng)前這個端口處于CLOSE_WAIT狀態(tài)!
在調(diào)用
sockConnected = socket(AF_INET, SOCK_STREAM, 0);
之后,我們要設(shè)置該套接字的選項來重用:
/// 允許重用本地地址和端口:
/// 這樣的好處是,即使socket斷了,調(diào)用前面的socket函數(shù)也不會占用另一個,而是始終就是一個端口
/// 這樣防止socket始終連接不上,那么按照原來的做法,會不斷地?fù)Q端口。
int nREUSEADDR = 1;
setsockopt(sockConnected,
????????????? SOL_SOCKET,
????????????? SO_REUSEADDR,
????????????? (const char*)&nREUSEADDR,
????????????? sizeof(int));
?
教科書上是這么說的:這樣,假如服務(wù)器關(guān)閉或者退出,造成本地地址和端口都處于TIME_WAIT狀態(tài),那么SO_REUSEADDR就顯得非常有用。
也許我們無法避免被凍結(jié)在CLOSE_WAIT狀態(tài)永遠(yuǎn)不出現(xiàn),但起碼可以保證不會占用新的端口。
其次,我們要設(shè)置SO_LINGER套接字選項:
從容關(guān)閉還是強(qiáng)行關(guān)閉?
LINGER是“拖延”的意思。
默認(rèn)情況下(Win2k),SO_DONTLINGER套接字選項的是1;SO_LINGER選項是,linger為{l_onoff:0,l_linger:0}。
如果在發(fā)送數(shù)據(jù)的過程中(send()沒有完成,還有數(shù)據(jù)沒發(fā)送)而調(diào)用了closesocket(),以前我們一般采取的措施是“從容關(guān)閉”:
因為在退出服務(wù)或者每次重新建立socket之前,我都會先調(diào)用
/// 先將雙向的通訊關(guān)閉
???? shutdown(sockConnected, SD_BOTH);
???? /// 安全起見,每次建立Socket連接前,先把這個舊連接關(guān)閉
closesocket(sockConnected);
我們這次要這么做:
設(shè)置SO_LINGER為零(亦即linger結(jié)構(gòu)中的l_onoff域設(shè)為非零,但l_linger為0),便不用擔(dān)心closesocket調(diào)用進(jìn)入“鎖定”狀態(tài)(等待完成),不論是否有排隊數(shù)據(jù)未發(fā)送或未被確認(rèn)。這種關(guān)閉方式稱為“強(qiáng)行關(guān)閉”,因為套接字的虛電路立即被復(fù)位,尚未發(fā)出的所有數(shù)據(jù)都會丟失。在遠(yuǎn)端的recv()調(diào)用都會失敗,并返回WSAECONNRESET錯誤。
在connect成功建立連接之后設(shè)置該選項:
linger m_sLinger;
m_sLinger.l_onoff = 1; // (在closesocket()調(diào)用,但是還有數(shù)據(jù)沒發(fā)送完畢的時候容許逗留)
m_sLinger.l_linger = 0; // (容許逗留的時間為0秒)
setsockopt(sockConnected,
???????? SOL_SOCKET,
???????? SO_LINGER,
???????? (const char*)&m_sLinger,
???????? sizeof(linger));
總結(jié)
也許我們避免不了CLOSE_WAIT狀態(tài)凍結(jié)的再次出現(xiàn),但我們會使影響降到最小,希望那個重用套接字選項能夠使得下一次重新建立連接時可以把CLOSE_WAIT狀態(tài)踢掉
總結(jié)
以上是生活随笔為你收集整理的TCP close_wait 状态的解释的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ACE入门---很好的文章
- 下一篇: 用ACE的Reactor模式实现网络通讯