如何優雅地關閉乙個socket

2021-09-30 04:38:22 字數 3444 閱讀 9728

如何優雅地關閉乙個socket 

1. 關閉socket時究竟做了什麼

關閉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狀態。

2. winsock2 api中的相關函式

先當然是查msdn,看到winsocks2 api中的相關函式有:closesocket,shutdown,wsasenddisconnect. 我大致說一下,具體詳細的資料還請自行查msdn.

intclosesocket(socket

s)的作用是關閉指定的socket,並且**其所有的資源。

intshutdown(socket

s,int

how)則是禁止在指定的socket s上禁止進行由how指定的操作,但並不對資源進行**,shutdown之後而closesocket之前s還不能再次connect或者wsaconnect.

intwsasenddisconnect(socket

s,lpwsabuf

lpoutbounddisconnectdata

)則和shutdown基本類似,稍有不同的就是wsasenddisconnect函式多了乙個

lpoutbounddisconnectdata

引數,可以允許傳送「斷開資料」(disconnect data).但msdn上寫了「the native implementation of tcp/ip on windows does not support disconnect data.」,所以一般我們就用shutdown函式就行了。

3. socket的優雅關閉

在msdn中對shutdown函式中的remarks部分有下面一段話,指出了如何進行一次優雅的slcket關閉:

call

wsaasyncselect

to register for fd_close notification.

callshutdownwith how=sd_send.

when fd_close received, call

recv

until zero returned, or socket_error.

call

closesocket

. closesocket的行為也是隨setsockopt()中引數的不同而有不同的表現,這裡影響它的行為的主要就是那個linger結構。

so_dontlinger 若為真,則so_linger選項被禁止。

so_linger 延遲關閉連線 struct linger

上面這兩個選項影響close行為

選項 間隔 關閉方式 等待關閉與否

so_dontlinger 不關心 優雅 否

so_linger 零 強制 否

so_linger 非零 優雅 是

若設定了so_linger(亦即linger結構中的l_onoff域設為非零),並設定了零超時間隔,則closesocket()不被阻塞立即執行,不論是否有排隊資料未傳送或未被確認。這種關閉方式稱為「強制」或「失效」關閉,因為套介面的虛電路立即被復位,且丟失了未傳送的資料。在遠端的recv()呼叫將以wsaeconnreset出錯。

若設定了so_linger並確定了非零的超時間隔,則closesocket()呼叫阻塞程序,直到所剩資料傳送完畢或超時。這種關閉稱為「優雅的」關閉。請注意如果套介面置為非阻塞且so_linger設為非零超時,則closesocket()呼叫將以wsaewouldblock錯誤返回。

若在乙個流類套介面上設定了so_dontlinger(也就是說將linger結構的l_onoff域設為零),則closesocket()呼叫立即返回。但是,如果可能,排隊的資料將在套介面關閉前傳送。請注意,在這種情況下windows套介面實現將在一段不確定的時間內保留套介面以及其他資源,這對於想用所以套介面的應用程式來說有一定影響。

所以一般來說,不應該把linger設定為so_linger 並且設定timeout為0,這樣的話,當本地主機呼叫closesocket時將會造成乙個「強制」或「失效」的非優雅關閉。可以根據實際情況設定為另外兩種情況。

如何優雅關閉乙個執行緒

當我們去thread類裡面找相關的介面時,發現有 個stop方法,看上去非常適合用來終止乙個執行緒,但是這個方法上面標了個 deprecated註解,非常明顯,這是乙個廢棄方法,不建議使用它。主要有兩個方面的原因 因為這個方法會將執行緒直接殺掉,沒有任何喘息機會,一旦執行緒被殺死,後面的 邏輯就再也...

如何優雅地關閉SparkStreaming

how to shutdown a spark streaming job gracefully 17 02 02 01 31 35 info streaming.streamingcontext invoking stop stopgracefully true from shutdown hoo...

如何優雅的生成乙個 diff patch

某天如果你要把一條 git 或 svn 記錄的修改傳送給其他人時我們要怎麼做呢?在 git 下你可以這麼做 git format patch commit sha1 id 1這樣會生成單條記錄的 patch 檔案,當然也可以生成多條或者兩條 commit 記錄之間的修改。例如我們在開源專案 nukl...