close和shutdown的区别
轉(zhuǎn)的,沒驗證
close(sock_fd)會把sock_fd的內(nèi)部計數(shù)器減1
當sock_fd的內(nèi)部計數(shù)器為0時, 才調(diào)用shutodwn(), 并最終釋放文件描述符
調(diào)用shutdown()只是進行了TCP斷開, 并沒有釋放文件描述符
本來正常的TCP程序不需要顯示調(diào)用shutdown()
但某些TCP程序十分不友好, 包括著名的firefox早期版本, 給服務(wù)器吃CLOSE_WAIT
調(diào)用shutodwn()就不會CLOSE_WAIT, 只會FIN_WAIT1或FIN_WAIT2
?
這就是服務(wù)器沒有調(diào)用shutdown引起的
客戶端非正常退出會給服務(wù)器帶來CLOSE_WAIT
CLOSE_WAIT非常討厭, 會卡住(阻塞)close()函數(shù)
?
?
以下描述主要是針對windows平臺下的TCP socket而言。
首先需要區(qū)分一下關(guān)閉socket和關(guān)閉TCP連接的區(qū)別,關(guān)閉TCP連接是指TCP協(xié)議層的東西,就是兩個TCP端之間交換了一些協(xié)議包(FIN,RST等),具體的交換過程可以看TCP協(xié)議,這里不詳細描述了。而關(guān)閉socket是指關(guān)閉用戶應(yīng)用程序中的socket句柄,釋放相關(guān)資源。但是當用戶關(guān)閉socket句柄時會隱含的觸發(fā)TCP連接的關(guān)閉過程。
TCP連接的關(guān)閉過程有兩種,一種是優(yōu)雅關(guān)閉(graceful close),一種是強制關(guān)閉(hard close或abortive close)。所謂優(yōu)雅關(guān)閉是指,如果發(fā)送緩存中還有數(shù)據(jù)未發(fā)出則其發(fā)出去,并且收到所有數(shù)據(jù)的ACK之后,發(fā)送FIN包,開始關(guān)閉過程。而強制關(guān)閉是指如果緩存中還有數(shù)據(jù),則這些數(shù)據(jù)都將被丟棄,然后發(fā)送RST包,直接重置TCP連接。
?
下面說一下shutdown及closesocket函數(shù)。
shutdown函數(shù)的原型是:
int shutdown(
??SOCKET s,
??int how
);
該函數(shù)用于關(guān)閉TCP連接,單并不關(guān)閉socket句柄。其第二個參數(shù)可以取三個值:SD_RECEIVE,SD_SEND,SD_BOTH。
SD_RECEIVE表明關(guān)閉接收通道,在該socket上不能再接收數(shù)據(jù),如果當前接收緩存中仍有未取出數(shù)據(jù)或者以后再有數(shù)據(jù)到達,則TCP會向發(fā)送端發(fā)送RST包,將連接重置。
SD_SEND表明關(guān)閉發(fā)送通道,TCP會將發(fā)送緩存中的數(shù)據(jù)都發(fā)送完畢并收到所有數(shù)據(jù)的ACK后向?qū)Χ税l(fā)送FIN包,表明本端沒有更多數(shù)據(jù)發(fā)送。這個是一個優(yōu)雅關(guān)閉過程。
SD_BOTH則表示同時關(guān)閉接收通道和發(fā)送通道。
?
From:?http://blog.csdn.net/bad_sheep/article/details/6157738
?
?
closesocket函數(shù)的原型是:
int closesocket(
??SOCKET s
);
該函數(shù)用于關(guān)閉socket句柄,并釋放相關(guān)資源。前面說過,關(guān)閉socket句柄時會隱含觸發(fā)TCP連接的關(guān)閉過程,那么closesocket觸發(fā)的是一個優(yōu)雅關(guān)閉過程還是強制關(guān)閉過程呢?這個與一個socket選項有關(guān):SO_LINGER 選項,該選項的設(shè)置值決定了closesocket的行為。該選項的參數(shù)值是linger結(jié)構(gòu),其定義是:
typedef struct linger {
??u_short l_onoff;
??u_short l_linger;
} linger;
當l_onoff值設(shè)置為0時,closesocket會立即返回,并關(guān)閉用戶socket句柄。如果此時緩沖區(qū)中有未發(fā)送數(shù)據(jù),則系統(tǒng)會在后臺將這些數(shù)據(jù)發(fā)送完畢后關(guān)閉TCP連接,是一個優(yōu)雅關(guān)閉過程,但是這里有一個副作用就是socket的底層資源會被保留直到TCP連接關(guān)閉,這個時間用戶應(yīng)用程序是無法控制的。
當l_onoff值設(shè)置為非0值,而l_linger也設(shè)置為0,那么closesocket也會立即返回并關(guān)閉用戶socket句柄,但是如果此時緩沖區(qū)中有未發(fā)送數(shù)據(jù),TCP會發(fā)送RST包重置連接,所有未發(fā)數(shù)據(jù)都將丟失,這是一個強制關(guān)閉過程。
當l_onoff值設(shè)置為非0值,而l_linger也設(shè)置為非0值時,同時如果socket是阻塞式的,此時如果緩沖區(qū)中有未發(fā)送數(shù)據(jù),如果TCP在l_linger表明的時間內(nèi)將所有數(shù)據(jù)發(fā)出,則發(fā)完后關(guān)閉TCP連接,這時是優(yōu)雅關(guān)閉過程;如果如果TCP在l_linger表明的時間內(nèi)沒有將所有數(shù)據(jù)發(fā)出,則會丟棄所有未發(fā)數(shù)據(jù)然后TCP發(fā)送RST包重置連接,此時就是一個強制關(guān)閉過程了。
另外還有一個socket選項SO_DONTLINGER,它的參數(shù)值是一個bool類型的,如果設(shè)置為true,則等價于SO_LINGER中將l_onoff設(shè)置為0。
注意SO_LINGER和SO_DONTLINGER選項只影響closesocket的行為,而與shutdown函數(shù)無關(guān),shutdown總是會立即返回的。
?
所以為了保證建議的最好的關(guān)閉方式是這樣的:
發(fā)送完了所有數(shù)據(jù)后:
(1)調(diào)用shutdown(s, SD_SEND),如果本端同時也接收數(shù)據(jù)時則執(zhí)行第二步,否則跳到第4步。
(2)繼續(xù)接收數(shù)據(jù),
(3)收到FD_CLOSE事件后,調(diào)用recv函數(shù)直到recv返回0或-1(保證收到所有數(shù)據(jù)),
(4)調(diào)用closesocket,關(guān)閉socket句柄。
?
在實際編程中,我們經(jīng)常也不調(diào)用shutdown,而是直接調(diào)用closesocket,利用closesocket隱含觸發(fā)TCP連接關(guān)閉過程的特性。此時的過程就是:
當發(fā)送完所有數(shù)據(jù)后:
(1)如果本端同時也接受數(shù)據(jù)則則執(zhí)行第二步,否則跳到第4步。
(2)繼續(xù)接收數(shù)據(jù),
(3)收到FD_CLOSE事件后,調(diào)用recv函數(shù)直到recv返回0或-1(保證收到所有數(shù)據(jù)),
(4)調(diào)用closesocket,關(guān)閉socket句柄。
但是此時為了保證數(shù)據(jù)不丟失,則需要設(shè)置SO_DONTLINGER選項,不過windows平臺下這個也是默認設(shè)置。
?
經(jīng)過實驗發(fā)現(xiàn),發(fā)送端應(yīng)用程序即便是異常退出或被kill掉進程,操作系統(tǒng)也不會丟棄發(fā)送緩沖區(qū)中的未發(fā)送數(shù)據(jù),而是會在后臺將這些數(shù)據(jù)發(fā)送出去。但是這是在socket的發(fā)送緩存不為0的前提下,當socket的發(fā)送緩存設(shè)置為0(通過SO_SNDBUF選項)時比較特殊,此時不論socket是否是阻塞的,send函數(shù)都會被阻塞直到傳入的用戶緩存中的數(shù)據(jù)都被發(fā)送出去并被確認,因為此時在驅(qū)動層沒有分配緩存存放用戶數(shù)據(jù),而是直接使用的應(yīng)用層的用戶緩存,所以必須阻塞直到數(shù)據(jù)都發(fā)出,否則可能會造成系統(tǒng)崩潰。
?
另外,如果是接收端的應(yīng)用程序異常退出或被kill掉進程,并且接收緩存中還有數(shù)據(jù)沒有取出的話,那么接收端的TCP會向發(fā)送端發(fā)送RST包,重置連接,因為后續(xù)數(shù)據(jù)已經(jīng)無法被提交應(yīng)用層了。
?
最后這里說一個感覺是windows的bug,就是做這樣的一個測試:
在一端線listen一個socket,然后在另一端connect,connect成功后,listen端會檢測到網(wǎng)絡(luò)事件觸發(fā),在listen端accept之前,將connect端kill掉,然后繼續(xù)運行l(wèi)isten端,listen端任然會accept成功,且在accept出來的socket發(fā)送數(shù)據(jù)也能成功。發(fā)送完之后在等網(wǎng)絡(luò)事件,此時又會等待成功,但是調(diào)用WSAEnumNetworkEvents得出的事件標識卻是0。之后再也不會等到網(wǎng)絡(luò)事件。
轉(zhuǎn)載于:https://www.cnblogs.com/liyulong1982/p/3990740.html
總結(jié)
以上是生活随笔為你收集整理的close和shutdown的区别的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一些Jquery操作
- 下一篇: Jmail的邮件发送