Socket調(diào)用Close后如何終止套接口的問題
在使用Socket調(diào)用過程中Close會出現(xiàn)會出現(xiàn)一些問題,當這些問題出現(xiàn)時我們應該如何去終止,本文為終止套接口問題提供了解決方案。
setsockopt 設置 SO_LINGER 選項
此選項指定函數(shù)close對面向連接的協(xié)議如何操作(如TCP)。內(nèi)核缺省close操作是立即返回,如果有數(shù)據(jù)殘留在套接口緩沖區(qū)中則系統(tǒng)將試著將這些數(shù)據(jù)發(fā)送給對方。
SO_LINGER選項用來改變此缺省設置。使用如下結(jié)構(gòu):
struct linger {
int l_onoff; /* 0 = off, nozero = on */
int l_linger; /* linger time */
};
有下列三種情況:
1、設置 l_onoff為0,則該選項關(guān)閉,l_linger的值被忽略,等于內(nèi)核缺省情況,close調(diào)用會立即返回給調(diào)用者,如果可能將會傳輸任何未發(fā)送的數(shù)據(jù);
2、設置 l_onoff為非0,l_linger為0,則套接口關(guān)閉時TCP夭折連接,TCP將丟棄保留在套接口發(fā)送緩沖區(qū)中的任何數(shù)據(jù)并發(fā)送一個RST給對方,而不是通常的四分組終止序列,這避免了TIME_WAIT狀態(tài);
3、設置 l_onoff 為非0,l_linger為非0,當套接口關(guān)閉時內(nèi)核將拖延一段時間(由l_linger決定)。如果套接口緩沖區(qū)中仍殘留數(shù)據(jù),進程將處于睡眠狀態(tài),直 到(a)所有數(shù)據(jù)發(fā)送完且被對方確認,之后進行正常的終止序列(描述字訪問計數(shù)為0)或(b)延遲時間到。此種情況下,應用程序檢查close的返回值是非常重要的,如果在數(shù)據(jù)發(fā)送完并被確認前時間到,close將返回EWOULDBLOCK錯誤且套接口發(fā)送緩沖區(qū)中的任何數(shù)據(jù)都丟失。close的成功返回僅告訴我們發(fā)送的數(shù)據(jù)(和FIN)已由對方TCP確認,它并不能告訴我們對方應用進程是否已讀了數(shù)據(jù)。如果套接口設為非阻塞的,它將不等待close完成。
注釋:l_linger的單位依賴于實現(xiàn): 4.4BSD假設其單位是時鐘滴答(百分之一秒),但Posix.1g規(guī)定單位為秒。
下面的代碼是一個使用SO_LINGER選項的例子,使用30秒的超時時限:
#define TRUE 1
#define FALSE 0
int z; /* Status code
*/ int s; /* Socket s */
struct linger so_linger;
...
so_linger.l_onoff = TRUE;
so_linger.l_linger = 30;
z = setsockopt(s,
SOL_SOCKET,
SO_LINGER,
&so_linger,
sizeof so_linger);
if ( z )
perror("setsockopt(2)");
下面的例子顯示了如何設置SO_LINGER的值來中止套接口s上的當前連接:
#define TRUE 1
#define FALSE 0
int z; /* Status code */
int s; /* Socket s */
struct linger so_linger;
...
so_linger.l_onoff = TRUE;
so_linger.l_linger = 0;
z = setsockopt(s,
SOL_SOCKET,
SO_LINGER,
&so_linger,
sizeof so_linger);
if ( z )
perror("setsockopt(2)");
close(s); /* Abort connection */
#p#
在上面的這個例子中,當調(diào)用close函數(shù)時,套接口s會立即中止。中止的語義是通過將超時值設置為0來實現(xiàn)的。
/********** WINDOWS **********/
/* 當連接中斷時,需要延遲關(guān)閉(linger)以保證所有數(shù)據(jù)都被傳輸,所以需要打開SO_LINGER這個選項;
* //注:大致意思就是說SO_LINGER選項用來設置當調(diào)用closesocket時是否馬上關(guān)閉socket;
* linger的結(jié)構(gòu)在/usr/include/linux/socket.h中定義://注:這個結(jié)構(gòu)就是SetSocketOpt中的Data的數(shù)據(jù)結(jié)構(gòu)
* struct linger
* {
* int l_onoff; /* Linger active */ //低字節(jié),0和非0,用來表示是否延時關(guān)閉socket
* int l_linger; /* How long to linger */ //高字節(jié),延時的時間數(shù),單位為秒
* };
* 如果l_onoff為0,則延遲關(guān)閉特性就被取消。
* 如果非零,則允許套接口延遲關(guān)閉; l_linger字段則指明延遲關(guān)閉的時間
*/
更具體的描述如下:
1、若設置了SO_LINGER(亦即linger結(jié)構(gòu)中的l_onoff域設為非零),并設置了零超時間隔,則closesocket()不被阻塞立即執(zhí)行,不論是否有排隊數(shù)據(jù)未發(fā)送或未被確認。這種關(guān)閉方式稱為“強制”或“失效”關(guān)閉,因為套接口的虛電路立即被復位,且丟失了未發(fā)送的數(shù)據(jù)。在遠端的recv()調(diào)用將以WSAECONNRESET出錯。
2、若設置了SO_LINGER并確定了非零的超時間隔,則closesocket()調(diào)用阻塞進程,直到所剩數(shù)據(jù)發(fā)送完畢或超時。這種關(guān)閉稱為“優(yōu)雅”或“從容”關(guān)閉。請注意如果套接口置為非阻塞且SO_LINGER設為非零超時,則closesocket()調(diào)用將以WSAEWOULDBLOCK錯誤返回。
3、若在一個流類套接口上設置了SO_DONTLINGER(也就是說將linger結(jié)構(gòu)的l_onoff域設為零),則closesocket()調(diào)用立即返回。但是,如果可能,排隊的數(shù)據(jù)將在套接口關(guān)閉前發(fā)送。請注意,在這種情況下WINDOWS套接口實現(xiàn)將在一段不確定的時間內(nèi)保留套接口以及其他資源,這對于想用所以套接口的應用程序來說有一定影響。
SO_DONTLINGER 若為真,則SO_LINGER選項被禁止。
SO_LINGER延遲關(guān)閉連接 struct linger上面這兩個選項影響close行為;
選項 間隔 關(guān)閉方式 等待關(guān)閉與否
SO_DONTLINGER 不關(guān)心 優(yōu)雅 否
SO_LINGER 零 強制 否
SO_LINGER 非零 優(yōu)雅 是