getsockopt
http://hi.baidu.com/wwwkljoel/item/a35e5745d14e02e6bcf45170
getsockopt,setsockopt用法小結(jié)
套接口選項
在前面的幾章中,我們討論了使用套接口的基礎(chǔ)內(nèi)容。現(xiàn)在我們要來探討一些可用的其他的特征。在我們掌握了這一章的概念之后,我們就為后面的套接口的高級主題做好了準(zhǔn)備。在這一章,我們將會專注于下列主題:
如何使用getsockopt(2)函數(shù)獲得套接口選項值
如何使用setsockopt(2)函數(shù)設(shè)置套接口選項值
如何使用這些常用的套接口選項
得到套接口選項
有時,一個程序需要確定為當(dāng)前為一個套接口進(jìn)行哪些選項設(shè)置。這對于一個子程序庫函數(shù)尤其如此,因為這個庫函數(shù)并不知道為這個套接口進(jìn)行哪些設(shè)置,而這個套接口需要作為一個參數(shù)進(jìn)行傳遞。程序也許需要知道類似于流默認(rèn)使用的緩沖區(qū)的大小。
允許我們得到套接口選項值的為getsockopt函數(shù)。這個函數(shù)的概要如下:
#include <sys/types.h>
#include <sys/socket.h>
int getsockopt(int s,
??? int level,
??? int optname,
??? void *optval,
??? socklen_t *optlen);
函數(shù)參數(shù)描述如下:
1 要進(jìn)行選項檢驗的套接口s
2 選項檢驗所在的協(xié)議層level
3 要檢驗的選項optname
4 指向接收選項值的緩沖區(qū)的指針optval
5 指針optlen同時指向輸入緩沖區(qū)的長度和返回的選項長度值
當(dāng)函數(shù)成功時返回0。當(dāng)發(fā)生錯誤時會返回-1,而錯誤原因會存放在外部變量errno中。
協(xié)議層參數(shù)指明了我們希望訪問一個選項所在的協(xié)議棧。通常我們需要使用下面中的一個:
SOL_SOCKET來訪問套接口層選項
SOL_TCP來訪問TCP層選項
我們在這一章的討論將會專注于SOL_SOCKET層選項的使用。
參數(shù)optname為一個整數(shù)值。在這里所使用的值首先是由所選用的level參數(shù)來確定的。在一個指定的協(xié)議層,optname參數(shù)將會確定我們希望訪問哪一個選項。下表列出了一些層與選項的組合值:
協(xié)議層?? ??? ?選項名字
SOL_SOCKET?? ?SO_REUSEADDR
SOL_SOCKET?? ?SO_KKEPALIVE
SOL_SOCKET?? ?SO_LINGER
SOL_SOCKET?? ?SO_BROADCAST
SOL_SOCKET?? ?SO_OOBINLINE
SOL_SOCKET?? ?SO_SNDBUF
SOL_SOCKET?? ?SO_RCVBUF
SOL_SOCKET?? ?SO_TYPE
SOL_SOCKET?? ?SO_ERROR
SOL_TCP?? ??? ?SO_NODELAY
上表所列的大多數(shù)選項為套接口選項,其中的層是由SOL_SOCKET指定的。為了比較的目的包含了一個TCP層套接口選項,其中的層是由SOL_TCP指定的。
大多數(shù)套接口選項獲得后存放在int數(shù)據(jù)類型中。當(dāng)查看手冊頁時,數(shù)據(jù)類型int通常會有一些假設(shè),除非表明了其他東西。當(dāng)使用一個布爾值時,當(dāng)值為非零時,int表示TRUE,而如果為零,則表示FALSE。
應(yīng)用getsockopt(2)
在這一部分,我們將會編譯并運行一個getsndrcv.c的程序,這個程序會獲得并報告一個套接口的發(fā)送以及接收緩沖區(qū)的大小尺寸。
/*getsndrc.v
?*
?* Get SO_SNDBUF & SO_RCVBUF Options:
?*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <assert.h>
/*
?* This function report the error and
?* exits back to the shell:
?*/
static void bail(const char *on_what)
{
??? if(errno != 0)
??? {
?? ?fputs(strerror(errno),stderr);
?? ?fputs(": ",stderr);
??? }
??? fputs(on_what,stderr);
??? fputc('\n',stderr);
??? exit(1);
}
int main(int argc,char **argv)
{
??? int z;
??? int s=-1;?? ??? ??? ?/* Socket */
??? int sndbuf=0;?? ??? ?/* Send buffer size */
??? int rcvbuf=0;?? ??? ?/* Receive buffer size */
??? socklen_t optlen;?? ??? ?/* Option length */
??? /*
???? * Create a TCP/IP socket to use:
???? */
??? s = socket(PF_INET,SOCK_STREAM,0);
??? if(s==-1)
?? ?bail("socket(2)");
??? /*
???? * Get socket option SO_SNDBUF:
???? */
??? optlen = sizeof sndbuf;
??? z = getsockopt(s,SOL_SOCKET,SO_SNDBUF,&sndbuf,&optlen);
??? if(z)
?? ?bail("getsockopt(s,SOL_SOCKET,"
?? ??? ?"SO_SNDBUF)");
??? assert(optlen == sizeof sndbuf);
??? /*
???? * Get socket option SON_RCVBUF:
???? */
??? optlen = sizeof rcvbuf;
??? z = getsockopt(s,SOL_SOCKET,SO_RCVBUF,&rcvbuf,&optlen);
??? if(z)
?? ?bail("getsockopt(s,SOL_SOCKET,"
?? ??? ?"SO_RCVBUF)");
??? assert(optlen == sizeof rcvbuf);
??? /*
???? * Report the buffer sizes:
???? */
??? printf("Socket s: %d\n",s);
??? printf("Send buf: %d bytes\n",sndbuf);
??? printf("Recv buf: %d bytes\n",rcvbuf);
??? close(s);
??? return 0;
}
程序的運行結(jié)果如下:
$ ./getsndrcv
socket s : 3
? Send buf: 65535 bytes
? Recv buf: 65535 bytes
設(shè)置套接口選項
如果認(rèn)為套接口的默認(rèn)發(fā)送以及接收緩沖區(qū)的尺寸太大時,作為程序設(shè)計者的我們可以將其設(shè)計為一個小的緩沖區(qū)。當(dāng)我們程序一個程序的幾個實例同時運行在我們的系統(tǒng)上時,這顯得尤其重要。
可以通過setsockopt(2)函數(shù)來設(shè)計套接口選項。這個函數(shù)的概要如下:
#include <sys/types.h>
#include <sys/socket.h>
int setsockopt(int s,
??? int level,
??? int optname,
??? const void *optval,
??? socklen_t optlen);
這個函數(shù)與我們在上面所討論的getsockopt函數(shù)類似,setsockopt函數(shù)的參數(shù)描述如下:
1 選項改變所要影響的套接口s
2 選項的套接口層次level
3 要設(shè)計的選項名optname
4 指向要為新選項所設(shè)置的值的指針optval
5 選項值長度optlen
這個函數(shù)參數(shù)與上面的getsockopt函數(shù)的參數(shù)的區(qū)別就在于最后一個參數(shù)僅是傳遞參數(shù)值。在這種情況下只是一個輸入值。
應(yīng)用setsockopt函數(shù)
下面的例子代碼為一個套接口改變了發(fā)送以及接收緩沖區(qū)的尺寸。在設(shè)置完這些選項以后,程序會得到并報告實際的緩沖區(qū)尺寸。
/*setsndrcv.c
?*
?* Set SO_SNDBUF & SO_RCVBUF Options:
?*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <assert.h>
/*
?* This function report the error and
?* exits back to the shell:
?*/
static void bail(const char *on_what)
{
??? if(errno!=0)
??? {
?? ?fputs(strerror(errno),stderr);
?? ?fputs(": ",stderr);
??? }
??? fputs(on_what,stderr);
??? fputc('\n',stderr);
??? exit(1);
}
int main(int argc,char **argv)
{
??? int z;
??? int s=-1;?? ??? ??? ?/* Socket */
??? int sndbuf=0;?? ??? ?/* Send buffer size */
??? int rcvbuf=0;?? ??? ?/* Receive buffer size */
??? socklen_t optlen;?? ??? ?/* Option length */
??? /*
???? * Create a TCP/IP socket to use:
???? */
??? s = socket(PF_INET,SOCK_STREAM,0);
??? if(s==-1)
?? ?bail("socket(2)");
??? /*
???? * set the SO_SNDBUF size :
???? */
??? sndbuf = 5000;?? ?/* Send buffer size */
??? z = setsockopt(s,SOL_SOCKET,SO_SNDBUF,&sndbuf,sizeof sndbuf);
??? if(z)
?? ?bail("setsockopt(s,SOL_SOCKET,"
?? ??? ?"SO_SNDBUF)");
??? /*
???? * Set the SO_RCVBUF size:
???? */
??? rcvbuf = 8192;?? ?/* Receive buffer size */
??? z = setsockopt(s,SOL_SOCKET,SO_RCVBUF,&rcvbuf,sizeof rcvbuf);
??? if(z)
?? ?bail("setsockopt(s,SOL_SOCKET,"
?? ??? ?"SO_RCVBUF)");
??? /*
???? * As a check on the above ....
???? * Get socket option SO_SNDBUF:
???? */
??? optlen = sizeof sndbuf;
??? z = getsockopt(s,SOL_SOCKET,SO_SNDBUF,&sndbuf,&optlen);
??? if(z)
?? ?bail("getsockopt(s,SOL_SOCKET,"
?? ??? ?"SO_SNDBUF)");
??? assert(optlen == sizeof sndbuf);
??? /*
???? * Get socket option SO_RCVBUF:
???? */
??? optlen = sizeof rcvbuf;
??? z = getsockopt(s,SOL_SOCKET,SO_RCVBUF,&rcvbuf,&optlen);
??? if(z)
?? ?bail("getsockopt(s,SOL_SOCKET"
?? ??? ?"SO_RCVBUF)");
??? assert(optlen == sizeof rcvbuf);
??? /*
???? * Report the buffer sizes:
???? */
??? printf("Socket s: %d\n",s);
??? printf(" Send buf: %d bytes\n",sndbuf);
??? printf(" Recv buf: %d bytes\n",rcvbuf);
??? close(s);
??? return 0;
}
程序的運行結(jié)果如下:
$ ./setsndrcv
Socket s : 3
? Send buf: 10000 bytes
? Recv buf: 16384 bytes
$
在這里我們要注意程序所報告的結(jié)果。他們看上去似乎是所指定的原始尺寸的兩倍。這個原因可以由Linux內(nèi)核源碼模塊net/core/sock.c中查到。我們可以查看一下SO_SNDBUF以及SO_RCVBUF的case語句。下面一段是由內(nèi)核模塊sock.c中摘錄的一段處理SO_SNDBUF的代碼:
398?? ??? ?case SO_SNDBUF:
399???????????????????????? /* Don't error on this BSD doesn't and if you think
400??????????????????????????? about it this is right. Otherwise apps have to
401??????????????????????????? play 'guess the biggest size' games. RCVBUF/SNDBUF
402??????????????????????????? are treated in BSD as hints */
403?????????????????????????? ?
404???????????????????????? if (val > sysctl_wmem_max)
405???????????????????????????????? val = sysctl_wmem_max;
406 set_sndbuf:
407???????????????????????? sk->sk_userlocks |= SOCK_SNDBUF_LOCK;
408???????????????????????? if ((val * 2) < SOCK_MIN_SNDBUF)
409???????????????????????????????? sk->sk_sndbuf = SOCK_MIN_SNDBUF;
410???????????????????????? else
411???????????????????????????????? sk->sk_sndbuf = val * 2;
412?
413???????????????????????? /*
414????????????????????????? *????? Wake up sending tasks if we
415????????????????????????? *????? upped the value.
416????????????????????????? */
417???????????????????????? sk->sk_write_space(sk);
418???????????????????????? break;
由這段代碼我們可以看到實際發(fā)生在SO_SNDBUF上的事情:
1 檢測SO_SNDBUF選項值來確定他是否超過了緩沖區(qū)的最大值
2 如果步驟1中的SO_SNDBUF選項值沒有超過最大值,那么就使用這個最大值,而不會向調(diào)用者返回錯誤代碼
3 如果SO_SNDBUF選項值的2倍小于套接口SO_SNDBUF的最小值,那么實際的SO_SNDBUF則會設(shè)置為SO_SNDBUF的最小值,否則則會SO_SNDBUF選項值則會設(shè)置為SO_SNDBUF選項值的2倍
從這里我們可以看出SO_SNDBUF的選項值只是所用的一個提示值。內(nèi)核會最終確定為SO_SNDBUF所用的最佳值。
查看更多的內(nèi)核源碼,我們可以看到類似的情況也適用于SO_RCVBUF選項。如下面的一段摘錄的代碼:
427???????????????? case SO_RCVBUF:
428???????????????????????? /* Don't error on this BSD doesn't and if you think
429??????????????????????????? about it this is right. Otherwise apps have to
430??????????????????????????? play 'guess the biggest size' games. RCVBUF/SNDBUF
431??????????????????????????? are treated in BSD as hints */
432????????????????????????? ?
433???????????????????????? if (val > sysctl_rmem_max)
434???????????????????????????????? val = sysctl_rmem_max;
435 set_rcvbuf:
436???????????????????????? sk->sk_userlocks |= SOCK_RCVBUF_LOCK;
437???????????????????????? /*
438????????????????????????? * We double it on the way in to account for
439????????????????????????? * "struct sk_buff" etc. overhead.?? Applications
440????????????????????????? * assume that the SO_RCVBUF setting they make will
441????????????????????????? * allow that much actual data to be received on that
442????????????????????????? * socket.
443????????????????????????? *
444????????????????????????? * Applications are unaware that "struct sk_buff" and
445????????????????????????? * other overheads allocate from the receive buffer
446????????????????????????? * during socket buffer allocation.
447????????????????????????? *
448????????????????????????? * And after considering the possible alternatives,
449????????????????????????? * returning the value we actually used in getsockopt
450????????????????????????? * is the most desirable behavior.
451????????????????????????? */
452???????????????????????? if ((val * 2) < SOCK_MIN_RCVBUF)
453???????????????????????????????? sk->sk_rcvbuf = SOCK_MIN_RCVBUF;
454???????????????????????? else
455???????????????????????????????? sk->sk_rcvbuf = val * 2;
456???????????????????????? break;
取得套接口類型
實際上我們只可以得到一些套接口選項。SO_TYPE就是其中的一例。這個選項會允許傳遞套接口的一個子函數(shù)來確定正在處理的是哪一種套接口類型。
如下面是一段得到套接口s類型的示例代碼:
/*gettype.c
?*
?* Get SO_TYPE Option:
?*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <assert.h>
/*
?* This function report the error and
?* exits back to the shell:
?*/
static void bail(const char *on_what)
{
??? if(errno!=0)
??? {
?? ?fputs(strerror(errno),stderr);
?? ?fputs(": ",stderr);
??? }
??? fputs(on_what,stderr);
??? fputc('\n',stderr);
??? exit(1);
}
int main(int argc,char **argv)
{
??? int z;
??? int s = -1;?? ??? ??? ?/* Socket */
??? int so_type = -1;?? ??? ?/* Socket type */
??? socklen_t optlen;?? ??? ?/* Option length */
??? /*
???? * Create a TCT/IP socket to use:
???? */
??? s = socket(PF_INET,SOCK_STREAM,0);
??? if(s==-1)
?? ?bail("socket(2)");
??? /*
???? * Get socket option SO_TYPE:
???? */
??? optlen = sizeof so_type;
??? z = getsockopt(s,SOL_SOCKET,SO_TYPE,&so_type,&optlen);
??? if(z)
?? ?bail("getsockopt(s,SOL_SOCKET,"
?? ??? ?"SO_TYPE)");
??? assert(optlen == sizeof so_type);
??? /*
???? * Report the result:
???? */
??? printf("Socket s: %d\n",s);
??? printf(" SO_TYPE : %d\n",so_type);
??? printf(" SO_STREAM = %d\n",SOCK_STREAM);
??? close(s);
??? return 0;
}
程序的運行結(jié)果如下:
$./gettype
Socket s: 3
?SO_TYPE : 1
?SO_STREAM = 1
設(shè)置SO_REUSEADDR選項
在第11章,"并發(fā)客戶端服務(wù)器"的第一部分中,提供并測試了一個使用fork系統(tǒng)調(diào)用設(shè)計的服務(wù)器。圖12.1顯示了在一個telnet命令與服務(wù)器建立連接之后的三個步驟。
這些步驟如下:
1 啟動服務(wù)器進(jìn)程(PID 926)。他監(jiān)聽客戶端連接。
2 啟動客戶端進(jìn)程(telnet命令),并且連接到服務(wù)器進(jìn)程(PID 926)。
3 通過fork調(diào)用創(chuàng)建服務(wù)器子進(jìn)程,這會保留的原始的父進(jìn)程(PID 926)并且創(chuàng)建一個新的子進(jìn)程(PID 927)。
4 連接的客戶端套接口由服務(wù)器父進(jìn)程(PID 926)關(guān)閉,僅在子進(jìn)程(PID 927)中保持連接的客戶端套接口處理打開狀態(tài)。
5 telnet命令與服務(wù)器子進(jìn)程(PID 927)隨意交互,而獨立于父進(jìn)程(PID 926)。
在步驟5,有兩個套接口活動:
服務(wù)器(PID 926)監(jiān)聽192.168.0.1:9099
客戶端由套接口192.168.0.1:9099進(jìn)行服務(wù)(PID 927),他連接到客戶端地址192.168.0.2:1035
客戶端由進(jìn)程ID 927進(jìn)行服務(wù)。這意味著我們可以殺掉進(jìn)程ID 926,而客戶端仍可以繼續(xù)被服務(wù)。然而,卻不會有新的連接連接到服務(wù)器,因為并沒有服務(wù)器監(jiān)聽新的連接(監(jiān)聽服務(wù)器PID 926已被殺死)
現(xiàn)在如果我們重啟服務(wù)器來監(jiān)聽新的連接,就會出現(xiàn)問題。當(dāng)新的服務(wù)器進(jìn)程試著綁定IP地址192.168.0.1:9099時,bind函數(shù)就會返回 EADDRINUSE的錯誤代碼。這個錯誤代碼表明IP已經(jīng)在9099端口上使用。這是因為進(jìn)程PID 927仍然在忙于服務(wù)一個客戶端。地址192.168.0.1:9099仍為這個進(jìn)程所使用。
這個問題的解決辦法就是殺掉進(jìn)程927,這個關(guān)閉套接口并且釋放IP地址和端口。然而,如果正在被服務(wù)的客戶是我們所在公司的CEO,這樣的做法似乎不是一個選擇。同時,其他的部門也會抱怨我們?yōu)槭裁匆匦聠臃?wù)器。
這個問題的一個好的解決辦法就是使用SO_REUSEADDR套接口選項。所有的服務(wù)器都應(yīng)使用這個選項,除非有一個更好的理由不使用。為了有效的使用這個選項,我們應(yīng)在監(jiān)聽連接的服務(wù)器中執(zhí)行下面的操作:
1 使用通常的socket函數(shù)創(chuàng)建一個監(jiān)聽套接口
2 調(diào)用setsockopt函數(shù)設(shè)置SO_REUSEADDR為TRUE
3 調(diào)用bind函數(shù)
套接口現(xiàn)在被標(biāo)記為可重用。如果監(jiān)聽服務(wù)器進(jìn)程因為任何原因終止,我們可以重新啟動這個服務(wù)器。當(dāng)一個客戶正為另一個服務(wù)器進(jìn)程使用同一個IP和端口號進(jìn)行服務(wù)時尤其如此。
為了有效的使用SO_REUSEADDR選項,需要考慮下面的情況:
在監(jiān)聽模式下并沒有同樣的IP地址和端口號的其他套接口
所有的同一個IP地址和端口號的套接口必須將SO_REUSEADDR選項設(shè)置為TRUE
這就意味著一個指定的IP地址和端口號對上只可以用一個監(jiān)聽器。如果這樣的套接口已經(jīng)存在,那么設(shè)置這樣的選項將不會達(dá)到我們的目的。
只有所有存在的同一個地址和端口號的套接口有這個選項設(shè)置,將SO_REUSEADDR設(shè)置為TRUE才會有效。如果存在的套接口沒有這個選項設(shè)置,那么bind函數(shù)就會繼續(xù)并且會返回一個錯誤號。
下面的代碼顯示如何將這個選項設(shè)置為TRUE:
#define TRUE 1
#define FALSE 0
int z;????? /* Status code */
int s;??? /* Socket number */
int so_reuseaddr = TRUE;
z = setsockopt(s,SOL_SOCKET,SO_REUSEADDR,
??? &so_reuseaddr,
??? sizeof so_reuseaddr);
如果需要SO_REUSEADDR選項可以由getsockopt函數(shù)進(jìn)行查詢。
設(shè)置SO_LINGER選項
另一個常用的選項就是SO_LINGER選項。與SO_REUSEADDR選項所不同的是這個選項所用的數(shù)據(jù)類型并不是一個簡單的int類型。
SO_LINGER選項的目的是控制當(dāng)調(diào)用close函數(shù)時套接口如何關(guān)閉。這個選項只適用于面向連接的協(xié)議,例如TCP。
內(nèi)核的默認(rèn)行為是允許close函數(shù)立即返回給調(diào)用者。如果可能任何未發(fā)送的TCP/IP數(shù)據(jù)將會進(jìn)行傳送,但是并不會保證這樣做。因為close函數(shù)會立即向調(diào)用者返回控制權(quán),程序并沒有辦法知道最后一位的數(shù)據(jù)是否進(jìn)行了發(fā)送。
SO_LINGER選項可以作用在套接口上,來使得程序阻塞close函數(shù)調(diào)用,直到所有最后的數(shù)據(jù)傳送到遠(yuǎn)程端。而且,這會保證兩端的調(diào)用知道套接口正常關(guān)閉。如果失敗,指定的選項超時,并且向調(diào)用程序返回一個錯誤。
通過使用不同的SO_LINGER選項值,可以應(yīng)用一個最后場景。如果調(diào)用程序希望立即中止通信,可以在linger結(jié)構(gòu)中設(shè)置合適的值。然后,一個到close的調(diào)用會初始化一個通信中止連接,而丟棄所有未發(fā)送的數(shù)據(jù),并立即關(guān)閉套接口。
SO_LINGER的這種操作模式是由linger結(jié)構(gòu)來控制的:
struct linger {
??? int??? l_onoff;
??? int??? l_linger;
};
成員l_onoff為一個布爾值,非零值表示TRUE,而零則表示FALSE。這個選項的三個值描述如下:
1 設(shè)置l_onoff為FALSE使得成員l_linger被忽略,而使用默認(rèn)的close行為。也就是說,close調(diào)用會立即返回給調(diào)用者,如果可能將會傳輸任何未發(fā)送的數(shù)據(jù)。
2 設(shè)置l_onoff為TRUE將會使得成員l_linger的值變得重要。當(dāng)l_linger非零時,這代表應(yīng)用在close函數(shù)調(diào)用上的以秒計的超時時限。如果超時發(fā)生之前,有未發(fā)送的數(shù)據(jù)并且成功關(guān)閉,函數(shù)將會成功返回。否則,將會返回錯誤,而將變量errno的值設(shè)置為EWOULDBLOCK。
3 將l_onoff設(shè)置為TRUE而l_linger設(shè)置為零時使得連接中止,在調(diào)用close時任何示發(fā)送的數(shù)據(jù)都會丟棄。
我們也許希望得到一些建議,在我們的程序中使用SO_LINGER選項,并且提供一個合理的超時時限。然后,可以檢測由close函數(shù)的返回值來確定連接是否成功關(guān)閉。如果返回了一個錯誤,這就告知我們的程序也許遠(yuǎn)程程序并不能接收我們發(fā)送的全部數(shù)據(jù)。相對的,他也許僅意味著連接關(guān)閉時發(fā)生的問題。
然而,我們必須保持清醒,這樣的方法在一些服務(wù)器設(shè)計中會產(chǎn)生新的問題。當(dāng)在close函數(shù)調(diào)用上將SO_LINGER選項配置為超時(linger),當(dāng)我們的服務(wù)器在close函數(shù)調(diào)用中執(zhí)行超時時會阻止其他的客戶端進(jìn)行服務(wù)。如果我們正在一個進(jìn)程中服務(wù)多個客戶端進(jìn)程時就會存在這個問題。使用默認(rèn)的行為也許更為合適,因為這允許close函數(shù)立即返回。而任何未發(fā)送的數(shù)據(jù)也會為內(nèi)核繼續(xù)發(fā)送。
最后,如果程序或是服務(wù)器知道連接應(yīng)何時中止時可以使用中止行為。這也許適用于當(dāng)服務(wù)器認(rèn)為沒有訪問權(quán)限的用戶正試著進(jìn)行訪問的情況。這種情況下的客戶并不會得到特別的關(guān)注。
下面的代碼是一個使用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)");
下面的例子顯示了如何設(shè)置SO_LINGER的值來中止套接口s上的當(dāng)前連接:
#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 */
在上面的這個例子中,當(dāng)調(diào)用close函數(shù)時,套接口s會立即中止。中止的語義是通過將超時值設(shè)置為0來實現(xiàn)的。
設(shè)置SO_KKEPALIVE選項
當(dāng)使用連接時,有時他們會空閑相當(dāng)長的時間。例如,建立一個telnet會話通過訪問股票交易服務(wù)。他也許會執(zhí)行一些初始的查詢,然后離開連接而保持服務(wù)打開,因為他希望回來查詢更多的內(nèi)容。然而,同時連接處理空閑狀態(tài),也許一次就是一個小時。
任何一個服務(wù)器認(rèn)為他有一個連接的客戶時會為其分配相應(yīng)的資源。如果服務(wù)器是一個派生類型(fork),那么整個Linux進(jìn)程及其相應(yīng)的內(nèi)存都分配給這個客戶。如果事情順利,這個場景并不會產(chǎn)生任何問題。然而當(dāng)出現(xiàn)網(wǎng)絡(luò)崩潰時,困難出現(xiàn)了,我們所有的578個客戶都會從我們的股票交易服務(wù)中失去連接。
在網(wǎng)絡(luò)服務(wù)恢復(fù)后,578個客戶會試著連接到我們的服務(wù)器,重建連接。這對于我們來說是一個真實的問題,因為我們的服務(wù)器在之前并沒有意識到他失去了空閑客戶--SO_KKEPALIVE來解決這個問題。
下面的例子顯示了如何在套接口s上使用SO_KKEPALIVE選項,從而一個斷開的空閑連接可以被檢測到:
#define TRUE??? 1
#define FALSE?? 0
int z; /* Status code */ int s; /* Socket s */
int so_keepalive;
...
so_keepalive = TRUE;
z = setsockopt(s,
??? SOL_SOCKET,
??? SO_KEEPALIVE,
??? &so_keepalive,
??? sizeof so_keepalive);
if ( z )
??? perror("setsockopt(2)");
在上面的例子中設(shè)置了SO_KEEPALIVE選項,這樣當(dāng)套接口連接空閑相當(dāng)長的時間時,一個探測信息(probe message)就會發(fā)送到遠(yuǎn)程端。這通常是在兩個小時的無活動后完成的。對于一個保持活動的探測信息會有三個可能的反應(yīng)。他們分別是:
1 端會合適的返回表明一切正常。并沒有向程序返回任何指示信息,因為這是程序假定的開始。
2 端響應(yīng)表明他對連接一無所知。這表明端自上次通信以后與主機(jī)進(jìn)行重新連接。這樣當(dāng)下次套接口操作時會向程序返回ECONNRESET錯誤代碼。
3 端沒有響應(yīng)。在這種情況下,內(nèi)核也許做了幾次嘗試進(jìn)行連接。如果沒有響應(yīng)請求,TCP通常會在大約11分鐘內(nèi)放棄。當(dāng)這種情況發(fā)生時,在下次套接口操作時會返回ETIMEOUT錯誤。其他的錯誤,例如EHOSTUNREACH會在網(wǎng)絡(luò)不再能到達(dá)主機(jī)時返回。
SO_KEEPALIVE 所調(diào)用的時間框架會限制他通常的用處。探測信息也只在大約兩個小時的無活動后才會發(fā)送。然后,當(dāng)沒有響應(yīng)時,在連接返回錯誤時還需要另外的11分鐘。無論怎樣,這個選項確實允許探測空閑的無連接套接口,然后由服務(wù)器進(jìn)行關(guān)閉。相應(yīng)的,支持長空閑連接的服務(wù)器應(yīng)允許這個特征。
設(shè)置SO_BROADCAST選項
我們現(xiàn)在還沒有討論到使用UDP進(jìn)行廣播的主題。然而,我們很容易意識到廣播功能的誤用以及所造成的網(wǎng)絡(luò)災(zāi)難。為了避免在沒有計劃廣播時進(jìn)行廣播,套接口禁用了廣播功能。如果確實需要廣播,那么C程序員要為套接口的這個功能處理相應(yīng)的麻煩。
SO_BROADCAST是一個布爾標(biāo)志選項,由int數(shù)據(jù)類型進(jìn)行設(shè)置。下面的例子顯示了如何設(shè)置SO_BROADCAST選項:
#define TRUE??? 1
#define FALSE?? 0
int z; /* Status code */
int s;???? /* Socket s */
int so_broadcast;
...
so_broadcast = TRUE;
z = setsockopt(s,
??? SOL_SOCKET,
??? SO_BROADCAST,
??? &so_broadcast,
??? sizeof so_broadcast);
if ( z )
??? perror("setsockopt(2)");
如果要setsockopt函數(shù)返回零,套接口s已經(jīng)允許進(jìn)行廣播。然而在這里要注意的是所選用的套接口類型必須具有廣播功能,例如UDP套接口。
設(shè)置SO_OOBINLINE選項
在一些情況下,已發(fā)送的數(shù)據(jù)也許會超過所限制的數(shù)據(jù)量。通常,這些越界的數(shù)據(jù)是用不同于通常的數(shù)據(jù)接收函數(shù)來進(jìn)行接收的。然而有時卻更喜歡使用通常的方式來接收這些越界數(shù)據(jù)。當(dāng)選擇這種方法時,越界數(shù)據(jù)作為通常數(shù)據(jù)流的一部分在通常數(shù)據(jù)之前到達(dá)。
要使用這個特征,我們可以用下面的代碼來完成:
#define TRUE??? 1
#define FALSE?? 0
int z; /* Status code */
int s;???? /* Socket s */
int so_oobinline;
...
so_oobinline = TRUE;
z = setsockopt(s,
??? SOL_SOCKET,
??? SO_OOBINLINE,
??? &so_oobinline,
??? sizeof so_oobinline);
if ( z )
??? perror("setsockopt(2)");
在設(shè)置了SO_OOBINLINE選項之后,越界數(shù)據(jù)就會與通常數(shù)據(jù)一起接收。在這種方式下,所接收的越界數(shù)據(jù)與通常數(shù)據(jù)相同。
SO_PASSCRED與SO_PEERCRED選項
這些選項僅適用于PF_UNIX(PF_LOCAL)套接口。這些選項用來在當(dāng)前主機(jī)的本地套接口上控制與傳遞憑證。這也許是最難掌握的一個主題。就現(xiàn)在而言,我們只需要簡單的注意到,如果我們希望編寫服務(wù)本地主機(jī)客戶的服務(wù)程序時,我們也許會對這兩個選項感興趣。
總結(jié)
以上是生活随笔為你收集整理的getsockopt的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 发热门诊医疗服务监测数据上报系统
- 下一篇: 听完计算机讲座的感想,听讲座心得体会5篇