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