1. 程式人生 > >Linux Socket學習(十二)

Linux Socket學習(十二)

套介面選項

在前面的幾章中,我們討論了使用套介面的基礎內容。現在我們要來探討一些可用的其他的特徵。在我們掌握了這一章的概念之後,我們就為後面的套介面的高階主題做好了準備。在這一章,我們將會專注於下列主題:
如何使用getsockopt(2)函式獲得套介面選項值
如何使用setsockopt(2)函式設定套介面選項值
如何使用這些常用的套介面選項

得到套介面選項

有時,一個程式需要確定為當前為一個套介面進行哪些選項設定。這對於一個子程式庫函式尤其如此,因為這個庫函式並不知道為這個套介面進行哪些設定,而這個套介面需要作為一個引數進行傳遞。程式也許需要知道類似於流預設使用的緩衝區的大小。

允許我們得到套介面選項值的為getsockopt函式。這個函式的概要如下:
#include <sys/types.h>
#include <sys/socket.h>
int getsockopt(int s,
    int level,
    int optname,
    void *optval,
    socklen_t *optlen);
函式引數描述如下:
1 要進行選項檢驗的套介面s
2 選項檢驗所在的協議層level
3 要檢驗的選項optname
4 指向接收選項值的緩衝區的指標optval
5 指標optlen同時指向輸入緩衝區的長度和返回的選項長度值

當函式成功時返回0。當發生錯誤時會返回-1,而錯誤原因會存放在外部變數errno中。

協議層引數指明瞭我們希望訪問一個選項所在的協議棧。通常我們需要使用下面中的一個:
SOL_SOCKET來訪問套介面層選項
SOL_TCP來訪問TCP層選項

我們在這一章的討論將會專注於SOL_SOCKET層選項的使用。

引數optname為一個整數值。在這裡所使用的值首先是由所選用的level引數來確定的。在一個指定的協議層,optname引數將會確定我們希望訪問哪一個選項。下表列出了一些層與選項的組合值:

協議層        選項名字
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

上表所列的大多數選項為套介面選項,其中的層是由SOL_SOCKET指定的。為了比較的目的包含了一個TCP層套介面選項,其中的層是由SOL_TCP指定的。

大多數套介面選項獲得後存放在int資料型別中。當檢視手冊頁時,資料型別int通常會有一些假設,除非表明了其他東西。當使用一個布林值時,當值為非零時,int表示TRUE,而如果為零,則表示FALSE。

應用getsockopt(2)


在這一部分,我們將會編譯並執行一個getsndrcv.c的程式,這個程式會獲得並報告一個套介面的傳送以及接收緩衝區的大小尺寸。
/*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;
}
程式的執行結果如下:
$ ./getsndrcv
socket s : 3
  Send buf: 65535 bytes
  Recv buf: 65535 bytes

設定套介面選項


如果認為套介面的預設傳送以及接收緩衝區的尺寸太大時,作為程式設計者的我們可以將其設計為一個小的緩衝區。當我們程式一個程式的幾個例項同時執行在我們的系統上時,這顯得尤其重要。

可以通過setsockopt(2)函式來設計套介面選項。這個函式的概要如下:
#include <sys/types.h>
#include <sys/socket.h>
int setsockopt(int s,
    int level,
    int optname,
    const void *optval,
    socklen_t optlen);

這個函式與我們在上面所討論的getsockopt函式類似,setsockopt函式的引數描述如下:
1 選項改變所要影響的套介面s
2 選項的套介面層次level
3 要設計的選項名optname
4 指向要為新選項所設定的值的指標optval
5 選項值長度optlen

這個函式引數與上面的getsockopt函式的引數的區別就在於最後一個引數僅是傳遞引數值。在這種情況下只是一個輸入值。

應用setsockopt函式


下面的例子程式碼為一個套介面改變了傳送以及接收緩衝區的尺寸。在設定完這些選項以後,程式會得到並報告實際的緩衝區尺寸。
/*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;
}
程式的執行結果如下:
$ ./setsndrcv
Socket s : 3
  Send buf: 10000 bytes
  Recv buf: 16384 bytes
$

在這裡我們要注意程式所報告的結果。他們看上去似乎是所指定的原始尺寸的兩倍。這個原因可以由Linux核心原始碼模組net/core/sock.c中查到。我們可以檢視一下SO_SNDBUF以及SO_RCVBUF的case語句。下面一段是由核心模組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;

由這段程式碼我們可以看到實際發生在SO_SNDBUF上的事情:
1 檢測SO_SNDBUF選項值來確定他是否超過了緩衝區的最大值
2 如果步驟1中的SO_SNDBUF選項值沒有超過最大值,那麼就使用這個最大值,而不會向呼叫者返回錯誤程式碼
3 如果SO_SNDBUF選項值的2倍小於套介面SO_SNDBUF的最小值,那麼實際的SO_SNDBUF則會設定為SO_SNDBUF的最小值,否則則會SO_SNDBUF選項值則會設定為SO_SNDBUF選項值的2倍

從這裡我們可以看出SO_SNDBUF的選項值只是所用的一個提示值。核心會最終確定為SO_SNDBUF所用的最佳值。

檢視更多的核心原始碼,我們可以看到類似的情況也適用於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就是其中的一例。這個選項會允許傳遞套介面的一個子函式來確定正在處理的是哪一種套介面型別。

如下面是一段得到套介面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;
}
程式的執行結果如下:
$./gettype
Socket s: 3
 SO_TYPE : 1
 SO_STREAM = 1

設定SO_REUSEADDR選項

在第11章,"併發客戶端伺服器"的第一部分中,提供並測試了一個使用fork系統呼叫設計的伺服器。圖12.1顯示了在一個telnet命令與伺服器建立連線之後的三個步驟。
這些步驟如下:
1 啟動伺服器程序(PID 926)。他監聽客戶端連線。
2 啟動客戶端程序(telnet命令),並且連線到伺服器程序(PID 926)。
3 通過fork呼叫建立伺服器子程序,這會保留的原始的父程序(PID 926)並且建立一個新的子程序(PID 927)。
4 連線的客戶端套介面由伺服器父程序(PID 926)關閉,僅在子程序(PID 927)中保持連線的客戶端套介面處理開啟狀態。
5 telnet命令與伺服器子程序(PID 927)隨意互動,而獨立於父程序(PID 926)。

在步驟5,有兩個套介面活動:
伺服器(PID 926)監聽192.168.0.1:9099
客戶端由套介面192.168.0.1:9099進行服務(PID 927),他連線到客戶端地址192.168.0.2:1035

客戶端由程序ID 927進行服務。這意味著我們可以殺掉程序ID 926,而客戶端仍可以繼續被服務。然而,卻不會有新的連線連線到伺服器,因為並沒有伺服器監聽新的連線(監聽伺服器PID 926已被殺死)

現在如果我們重啟伺服器來監聽新的連線,就會出現問題。當新的伺服器程序試著繫結IP地址192.168.0.1:9099時,bind函式就會返回EADDRINUSE的錯誤程式碼。這個錯誤程式碼表明IP已經在9099埠上使用。這是因為程序PID 927仍然在忙於服務一個客戶端。地址192.168.0.1:9099仍為這個程序所使用。

這個問題的解決辦法就是殺掉程序927,這個關閉套介面並且釋放IP地址和埠。然而,如果正在被服務的客戶是我們所在公司的CEO,這樣的做法似乎不是一個選擇。同時,其他的部門也會抱怨我們為什麼要重新啟動伺服器。

這個問題的一個好的解決辦法就是使用SO_REUSEADDR套介面選項。所有的伺服器都應使用這個選項,除非有一個更好的理由不使用。為了有效的使用這個選項,我們應在監聽連線的伺服器中執行下面的操作:
1 使用通常的socket函式建立一個監聽套介面
2 呼叫setsockopt函式設定SO_REUSEADDR為TRUE
3 呼叫bind函式

套介面現在被標記為可重用。如果監聽伺服器程序因為任何原因終止,我們可以重新啟動這個伺服器。當一個客戶正為另一個伺服器程序使用同一個IP和埠號進行服務時尤其如此。

為了有效的使用SO_REUSEADDR選項,需要考慮下面的情況:
在監聽模式下並沒有同樣的IP地址和埠號的其他套介面
所有的同一個IP地址和埠號的套介面必須將SO_REUSEADDR選項設定為TRUE

這就意味著一個指定的IP地址和埠號對上只可以用一個監聽器。如果這樣的套介面已經存在,那麼設定這樣的選項將不會達到我們的目的。

只有所有存在的同一個地址和埠號的套介面有這個選項設定,將SO_REUSEADDR設定為TRUE才會有效。如果存在的套介面沒有這個選項設定,那麼bind函式就會繼續並且會返回一個錯誤號。

下面的程式碼顯示如何將這個選項設定為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函式進行查詢。

設定SO_LINGER選項

另一個常用的選項就是SO_LINGER選項。與SO_REUSEADDR選項所不同的是這個選項所用的資料型別並不是一個簡單的int型別。

SO_LINGER選項的目的是控制當呼叫close函式時套介面如何關閉。這個選項只適用於面向連線的協議,例如TCP。

核心的預設行為是允許close函式立即返回給呼叫者。如果可能任何未傳送的TCP/IP資料將會進行傳送,但是並不會保證這樣做。因為close函式會立即向呼叫者返回控制權,程式並沒有辦法知道最後一位的資料是否進行了傳送。

SO_LINGER選項可以作用在套介面上,來使得程式阻塞close函式呼叫,直到所有最後的資料傳送到遠端端。而且,這會保證兩端的呼叫知道套介面正常關閉。如果失敗,指定的選項超時,並且向呼叫程式返回一個錯誤。

通過使用不同的SO_LINGER選項值,可以應用一個最後場景。如果呼叫程式希望立即中止通訊,可以在linger結構中設定合適的值。然後,一個到close的呼叫會初始化一個通訊中止連線,而丟棄所有未傳送的資料,並立即關閉套介面。

SO_LINGER的這種操作模式是由linger結構來控制的:
struct linger {
    int    l_onoff;
    int    l_linger;
};
成員l_onoff為一個布林值,非零值表示TRUE,而零則表示FALSE。這個選項的三個值描述如下:

1 設定l_onoff為FALSE使得成員l_linger被忽略,而使用預設的close行為。也就是說,close呼叫會立即返回給呼叫者,如果可能將會傳輸任何未傳送的資料。
2 設定l_onoff為TRUE將會使得成員l_linger的值變得重要。當l_linger非零時,這代表應用在close函式呼叫上的以秒計的超時時限。如果超時發生之前,有未傳送的資料並且成功關閉,函式將會成功返回。否則,將會返回錯誤,而將變數errno的值設定為EWOULDBLOCK。
3 將l_onoff設定為TRUE而l_linger設定為零時使得連線中止,在呼叫close時任何示傳送的資料都會丟棄。

我們也許希望得到一些建議,在我們的程式中使用SO_LINGER選項,並且提供一個合理的超時時限。然後,可以檢測由close函式的返回值來確定連線是否成功關閉。如果返回了一個錯誤,這就告知我們的程式也許遠端程式並不能接收我們傳送的全部資料。相對的,他也許僅意味著連線關閉時發生的問題。

然而,我們必須保持清醒,這樣的方法在一些伺服器設計中會產生新的問題。當在close函式呼叫上將SO_LINGER選項配置為超時(linger),當我們的伺服器在close函式呼叫中執行超時時會阻止其他的客戶端進行服務。如果我們正在一個程序中服務多個客戶端程序時就會存在這個問題。使用預設的行為也許更為合適,因為這允許close函式立即返回。而任何未傳送的資料也會為核心繼續傳送。

最後,如果程式或是伺服器知道連線應何時中止時可以使用中止行為。這也許適用於當伺服器認為沒有訪問許可權的使用者正試著進行訪問的情況。這種情況下的客戶並不會得到特別的關注。

下面的程式碼是一個使用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 */

在上面的這個例子中,當呼叫close函式時,套介面s會立即中止。中止的語義是通過將超時值設定為0來實現的。

設定SO_KKEPALIVE選項

當使用連線時,有時他們會空閒相當長的時間。例如,建立一個telnet會話通過訪問股票交易服務。他也許會執行一些初始的查詢,然後離開連線而保持服務開啟,因為他希望回來查詢更多的內容。然而,同時連線處理空閒狀態,也許一次就是一個小時。

任何一個伺服器認為他有一個連線的客戶時會為其分配相應的資源。如果伺服器是一個派生型別(fork),那麼整個Linux程序及其相應的記憶體都分配給這個客戶。如果事情順利,這個場景並不會產生任何問題。然而當出現網路崩潰時,困難出現了,我們所有的578個客戶都會從我們的股票交易服務中失去連線。

在網路服務恢復後,578個客戶會試著連線到我們的伺服器,重建連線。這對於我們來說是一個真實的問題,因為我們的伺服器在之前並沒有意識到他失去了空閒客戶--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)");

在上面的例子中設定了SO_KEEPALIVE選項,這樣當套介面連線空閒相當長的時間時,一個探測資訊(probe message)就會發送到遠端端。這通常是在兩個小時的無活動後完成的。對於一個保持活動的探測資訊會有三個可能的反應。他們分別是:
1 端會合適的返回表明一切正常。並沒有向程式返回任何指示資訊,因為這是程式假定的開始。
2 端響應表明他對連線一無所知。這表明端自上次通訊以後與主機進行重新連線。這樣當下次套介面操作時會向程式返回ECONNRESET錯誤程式碼。
3 端沒有響應。在這種情況下,核心也許做了幾次嘗試進行連線。如果沒有響應請求,TCP通常會在大約11分鐘內放棄。當這種情況發生時,在下次套介面操作時會返回ETIMEOUT錯誤。其他的錯誤,例如EHOSTUNREACH會在網路不再能到達主機時返回。

SO_KEEPALIVE所呼叫的時間框架會限制他通常的用處。探測資訊也只在大約兩個小時的無活動後才會傳送。然後,當沒有響應時,在連線返回錯誤時還需要另外的11分鐘。無論怎樣,這個選項確實允許探測空閒的無連線套介面,然後由伺服器進行關閉。相應的,支援長空閒連線的伺服器應允許這個特徵。

設定SO_BROADCAST選項

我們現在還沒有討論到使用UDP進行廣播的主題。然而,我們很容易意識到廣播功能的誤用以及所造成的網路災難。為了避免在沒有計劃廣播時進行廣播,套介面禁用了廣播功能。如果確實需要廣播,那麼C程式設計師要為套介面的這個功能處理相應的麻煩。

SO_BROADCAST是一個布林標誌選項,由int資料型別進行設定。下面的例子顯示瞭如何設定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函式返回零,套介面s已經允許進行廣播。然而在這裡要注意的是所選用的套介面型別必須具有廣播功能,例如UDP套介面。

設定SO_OOBINLINE選項

在一些情況下,已傳送的資料也許會超過所限制的資料量。通常,這些越界的資料是用不同於通常的資料接收函式來進行接收的。然而有時卻更喜歡使用通常的方式來接收這些越界資料。當選擇這種方法時,越界資料作為通常資料流的一部分在通常資料之前到達。

要使用這個特徵,我們可以用下面的程式碼來完成:
#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)");
在設定了SO_OOBINLINE選項之後,越界資料就會與通常資料一起接收。在這種方式下,所接收的越界資料與通常資料相同。

SO_PASSCRED與SO_PEERCRED選項

這些選項僅適用於PF_UNIX(PF_LOCAL)套介面。這些選項用來在當前主機的本地套介面上控制與傳遞憑證。這也許是最難掌握的一個主題。就現在而言,我們只需要簡單的注意到,如果我們希望編寫服務本地主機客戶的服務程式時,我們也許會對這兩個選項感興趣。