日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

异步socket优雅的关闭-CancelIO和SO_LINGER

發布時間:2024/4/11 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 异步socket优雅的关闭-CancelIO和SO_LINGER 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
我們在利用IOCP(完成端口)進行程序設計的時候,經常要關閉一些不滿足條件的套接字。假如我們直接采用closesocket方法進行關閉的話,綁定到IO端口的此套接字的未發送的數據就會丟失,這種情況是我們不愿意發生的。下面介紹一種合理關閉此套接字的方法: ? 首先,利用setsockopt(MSDN)函數設定套接字的選項,我們把此套接字設定為:假如有數據未發送,當數據發送完后再關閉此套接字。 代碼如下: LINGER lingerStruct; lingerStruct.l_onoff = 1; lingerStruct.l_linger = 0; setsockopt(Socket, SOL_SOCKET, SO_LINGER,(char *)&lingerStruct, sizeof(lingerStruct) );// // Now close the socket handle. This will do an abortive or graceful close, as requested. CancelIo((HANDLE) Socket);closesocket(Socket); clientSocket = INVALID_SOCKET;
------------------------------------------------------------------------------------------------------------------------------------------

以下描述主要是針對windows平臺下的TCP socket而言。

首先需要區分一下關閉socket和關閉TCP連接的區別,關閉TCP連接是指TCP協議層的東西,就是兩個TCP端之間交換了一些協議包(FIN,RST等),具體的交換過程可以看TCP協議,這里不詳細描述了。而關閉socket是指關閉用戶應用程序中的socket句柄,釋放相關資源。但是當用戶關閉socket句柄時會隱含的觸發TCP連接的關閉過程。

TCP連接的關閉過程有兩種,一種是優雅關閉(graceful close),一種是強制關閉(hard close或abortive close)。所謂優雅關閉是指,如果發送緩存中還有數據未發出則其發出去,并且收到所有數據的ACK之后,發送FIN包,開始關閉過程。而強制關閉是指如果緩存中還有數據,則這些數據都將被丟棄,然后發送RST包,直接重置TCP連接。

?

下面說一下shutdown及closesocket函數。

shutdown函數的原型是:

int shutdown(

??SOCKET s,

??int how

);

該函數用于關閉TCP連接,單并不關閉socket句柄。其第二個參數可以取三個值:SD_RECEIVE,SD_SEND,SD_BOTH。

SD_RECEIVE表明關閉接收通道,在該socket上不能再接收數據,如果當前接收緩存中仍有未取出數據或者以后再有數據到達,則TCP會向發送端發送RST包,將連接重置。

SD_SEND表明關閉發送通道,TCP會將發送緩存中的數據都發送完畢并收到所有數據的ACK后向對端發送FIN包,表明本端沒有更多數據發送。這個是一個優雅關閉過程。

SD_BOTH則表示同時關閉接收通道和發送通道。

?

closesocket函數的原型是:

int closesocket(

??SOCKET s

);

該函數用于關閉socket句柄,并釋放相關資源。前面說過,關閉socket句柄時會隱含觸發TCP連接的關閉過程,那么closesocket觸發的是一個優雅關閉過程還是強制關閉過程呢?這個與一個socket選項有關:SO_LINGER 選項,該選項的設置值決定了closesocket的行為。該選項的參數值是linger結構,其定義是:

typedef struct linger {

??u_short l_onoff;

??u_short l_linger;

} linger;

當l_onoff值設置為0時,closesocket會立即返回,并關閉用戶socket句柄。如果此時緩沖區中有未發送數據,則系統會在后臺將這些數據發送完畢后關閉TCP連接,是一個優雅關閉過程,但是這里有一個副作用就是socket的底層資源會被保留直到TCP連接關閉,這個時間用戶應用程序是無法控制的。

當l_onoff值設置為非0值,而l_linger也設置為0,那么closesocket也會立即返回并關閉用戶socket句柄,但是如果此時緩沖區中有未發送數據,TCP會發送RST包重置連接,所有未發數據都將丟失,這是一個強制關閉過程。

當l_onoff值設置為非0值,而l_linger也設置為非0值時,同時如果socket是阻塞式的,此時如果緩沖區中有未發送數據,如果TCP在l_linger表明的時間內將所有數據發出,則發完后關閉TCP連接,這時是優雅關閉過程;如果如果TCP在l_linger表明的時間內沒有將所有數據發出,則會丟棄所有未發數據然后TCP發送RST包重置連接,此時就是一個強制關閉過程了。

另外還有一個socket選項SO_DONTLINGER,它的參數值是一個bool類型的,如果設置為true,則等價于SO_LINGER中將l_onoff設置為0。

注意SO_LINGER和SO_DONTLINGER選項只影響closesocket的行為,而與shutdown函數無關,shutdown總是會立即返回的。

?

所以為了保證建議的最好的關閉方式是這樣的:

發送完了所有數據后:

(1)調用shutdown(s, SD_SEND),如果本端同時也接收數據時則執行第二步,否則跳到第4步。

(2)繼續接收數據,

(3)收到FD_CLOSE事件后,調用recv函數直到recv返回0或-1(保證收到所有數據),

(4)調用closesocket,關閉socket句柄。

?

在實際編程中,我們經常也不調用shutdown,而是直接調用closesocket,利用closesocket隱含觸發TCP連接關閉過程的特性。此時的過程就是:

當發送完所有數據后:

(1)如果本端同時也接受數據則則執行第二步,否則跳到第4步。

(2)繼續接收數據,

(3)收到FD_CLOSE事件后,調用recv函數直到recv返回0或-1(保證收到所有數據),

(4)調用closesocket,關閉socket句柄。

但是此時為了保證數據不丟失,則需要設置SO_DONTLINGER選項,不過windows平臺下這個也是默認設置。

?

經過實驗發現,發送端應用程序即便是異常退出或被kill掉進程,操作系統也不會丟棄發送緩沖區中的未發送數據,而是會在后臺將這些數據發送出去。但是這是在socket的發送緩存不為0的前提下,當socket的發送緩存設置為0(通過SO_SNDBUF選項)時比較特殊,此時不論socket是否是阻塞的,send函數都會被阻塞直到傳入的用戶緩存中的數據都被發送出去并被確認,因為此時在驅動層沒有分配緩存存放用戶數據,而是直接使用的應用層的用戶緩存,所以必須阻塞直到數據都發出,否則可能會造成系統崩潰。

?

另外,如果是接收端的應用程序異常退出或被kill掉進程,并且接收緩存中還有數據沒有取出的話,那么接收端的TCP會向發送端發送RST包,重置連接,因為后續數據已經無法被提交應用層了。

?

最后這里說一個感覺是windows的bug,就是做這樣的一個測試:

在一端線listen一個socket,然后在另一端connect,connect成功后,listen端會檢測到網絡事件觸發,在listen端accept之前,將connect端kill掉,然后繼續運行listen端,listen端任然會accept成功,且在accept出來的socket發送數據也能成功。發送完之后在等網絡事件,此時又會等待成功,但是調用WSAEnumNetworkEvents得出的事件標識卻是0。之后再也不會等到網絡事件。


總結

以上是生活随笔為你收集整理的异步socket优雅的关闭-CancelIO和SO_LINGER的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。