小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

Linux Socket學(xué)習(xí)(八)...

 ShangShujie 2007-08-11
面向無連接的協(xié)議

直到這時,我們實際了忽略了套接口通信的大部分內(nèi)容。相反,我們關(guān)注于創(chuàng)建套接口,綁定地址以及關(guān)閉套接口?,F(xiàn)在我們要實際使用套接口了。

對于套接口有兩種基本的通信模式。他們是面向無連接的通信與面向連接的通信。

在這一章,我們將會了解下面內(nèi)容:
面向無連接通信與面向連接通信之間的區(qū)別
如何執(zhí)行無連接的輸入與輸出操作
如何編寫一個數(shù)據(jù)報服務(wù)器
如何編寫一個數(shù)據(jù)報客戶端

現(xiàn)在我們來關(guān)注一下面向無連接通信與面向連接通信之間的區(qū)別。

通信方法

正如我們所想到的,面向無連接的通信在通信開始之前并不需要建立連接。這就像一個拿著護(hù)音器在嘈雜中向一個人喊話一樣。對于每一次新的喊話,發(fā)送消息的人可以將他的話傳遞到另一個人,而不需要先前的同意。

相類似的,在我們創(chuàng)建一個無連接的套接口以后,我們可以向?qū)邮瘴覀兿⒌娜魏翁捉涌诎l(fā)送消息。此時并沒有建立連接,而每一個消息可以直接發(fā)送給一個不同的接收套接口。

理解其優(yōu)點

面向無連接的通信與面向連接的通信相比提供了一些優(yōu)點。這些優(yōu)點包括:
簡單:不需要建立連接
靈活:可以將消息發(fā)送到不同的帶有消息發(fā)送操作的接收端
高效:不要求連接的建立與關(guān)閉,而這需要向網(wǎng)絡(luò)添加大量的消息包
快速:因為不需要連接的建立也關(guān)閉,而只是發(fā)送消息本身
廣播功能:將一個消息發(fā)送到多個接收端的功能

一個面向無連接的協(xié)議的許多優(yōu)點都與高效與速度有關(guān)。為了建立連接,需要在數(shù)據(jù)交換之前在兩個端點之間交換大量的數(shù)據(jù)包。而一個已建立的通信頻道的關(guān)閉則需要額外的數(shù)據(jù)包交換。而這產(chǎn)生額外的時間花費。

無連接協(xié)議還有一個優(yōu)點就是廣播功能。他可以將一個消息發(fā)送到多個接收端。這高效的利用了網(wǎng)絡(luò)帶度。

在這一章,我們將會學(xué)習(xí)UDP協(xié)議(用戶數(shù)據(jù)報協(xié)議),這是一個面向無連接的協(xié)議。這個協(xié)議充分利用了我們前面所談到的所有優(yōu)點。

理解無連接通信的缺點

既然無連接的協(xié)議可以提供這些優(yōu)點,我們也許想要知道為什么不總是使用這種協(xié)議。任何事物,即使是無連接的通信也有他的缺點。

UDP協(xié)議是一個非常簡單的傳輸層協(xié)議,而且他是無連接的。對于UDP協(xié)議,存在著下面的缺點:
協(xié)議不可靠
沒有多個數(shù)據(jù)報的序列
消息尺寸存在限制

可 靠性問題對于大多數(shù)程序來說是最嚴(yán)重的限制。我們的程序可以將UDP數(shù)據(jù)報發(fā)送到網(wǎng)絡(luò),但是并不能保證接收端可以接收到。這個消息也許被丟棄,而沒有同一 網(wǎng)絡(luò)的接收端所接收。相應(yīng)的,由于網(wǎng)絡(luò)路徑中多個路由器中的一個不能接收沒有檢驗碼錯誤的消息,所發(fā)送的消息也會丟失。當(dāng)一個數(shù)據(jù)包接收出錯時,UDP包 只是簡單的丟棄并且永遠(yuǎn)的丟失。

其他的問題也會造成UDP數(shù)據(jù)包的丟失。如果接收主機(jī)或是路由器不能分配足夠的緩沖空間來存放UDP數(shù)據(jù)包,這些數(shù)據(jù)包也會丟失。因此,如果我們的UDP數(shù)所包需要傳輸很長的距離,這些數(shù)據(jù)包就很有可能沿途丟失。

在 我們決定為我們的程序使用UDP協(xié)議之前,還有另外一個問題需要考慮。如果我們成功的向我們的目的地發(fā)送了兩個或是多個數(shù)據(jù)報,很有可能這些數(shù)據(jù)報并不是 順序接收的。UDP協(xié)議使用IP協(xié)議來傳輸最終的數(shù)據(jù)報。IP數(shù)據(jù)包可以由每一個傳輸進(jìn)行不同的路由,而路由是依據(jù)當(dāng)前的網(wǎng)絡(luò)環(huán)境而變化的。這通常會造成 一些數(shù)據(jù)包在另一些數(shù)據(jù)包之前到達(dá)目的地。也就說UDP數(shù)據(jù)包會并不會按順序到達(dá)。

最后存在數(shù)據(jù)報尺寸的問題。理論上一個數(shù)據(jù)報的最大長 度要小于64KB。然而,許多UNIX主機(jī)所支持的最大長度僅為32KB。其他的UNIX內(nèi)核內(nèi)建的尺寸限制更小,只有8KB。最后,接收套接口程序會將 這個尺寸限制為接收緩沖區(qū)的尺寸。因為這個原因,一些程序?qū)DP的消息尺寸限制為512字節(jié)或是更小。

如果發(fā)送了一個大的UDP數(shù)據(jù) 包,則必須將這個數(shù)據(jù)包分解為一些小的IP片段,然后在接收端重新進(jìn)行組合。組合過程需要分配緩沖區(qū)來存放接收到的IP片段。這通常會指定一個超時時限, 直到整個數(shù)據(jù)包重新進(jìn)行組合。緩沖區(qū)的完整會造成重組的無約束,而這會造成我們的UDP數(shù)據(jù)包的丟失。UDP數(shù)據(jù)報在成功的到達(dá)目的地之前必須要經(jīng)歷些風(fēng) 險。

對于一些程序來說,這會將使得我們轉(zhuǎn)向更為可靠的面向連接的協(xié)議,例如TCP/IP。這些于其他的程序來說并不是嚴(yán)重的限制。當(dāng)然只有我們來決定UDP是否適用我們的程序。

執(zhí)行數(shù)據(jù)報輸入/輸出操作

在 前面的介紹中,我們看到了當(dāng)我們要從套接口中讀取或是向套接口中寫入時,我們使用了read(2)和write(2)函數(shù)。在當(dāng)時我們并沒有指現(xiàn), socketpair(2)函數(shù)使用了面向連接的協(xié)議來創(chuàng)建一對套接口。相應(yīng)的,我們可以使用我們所熟悉的read(2)與write(2)函數(shù)來在這些 套接口上執(zhí)行I/O操作。

然而,當(dāng)要發(fā)送與接收數(shù)據(jù)報時,需要一對不同的函數(shù)。這是因為每一個要發(fā)送的消息實際上要發(fā)送到一個不同的目的 地址。發(fā)送數(shù)據(jù)報的函數(shù)允許我們指定目的接收端的地址。同樣,當(dāng)我們要接收一個數(shù)據(jù)報時,我們也需要指明他的來源。這些新函數(shù)必須為我們提供一個合適的方 法來確定發(fā)送者的地址。

簡介sendto(2)函數(shù)

sendto(2)函數(shù)允許我們寫入一個數(shù)據(jù)報,同時指定目的接收端的地址。函數(shù)概要如下:
#include <sys/types.h>
#include <sys/socket.h>
int sendto(int s,
    const void *msg,
    int len,
    unsigned flags,
    const struct sockaddr *to,
    int tolen);

這些參數(shù)描述如下:
1 第一個參數(shù)s為套接口號。我們可以從socket函數(shù)得到這個值。
2 參數(shù)msg為指向存放我們將要發(fā)送的消息的緩沖區(qū)的指針
3 參數(shù)len為字節(jié)形式長度
4 flags允放我們指定一些選項位。在許多情況下,我們只需指定為0
5 參數(shù)to為指向我們已經(jīng)建立的通用套接口的指針。這是數(shù)據(jù)報接收端的地址
6 參數(shù)tolen為參數(shù)to地址的長度

當(dāng)sendto函數(shù)執(zhí)行成功時,會返回寫入的字節(jié)數(shù)。當(dāng)發(fā)生錯誤時,則會返回-1,同時錯誤號會記錄在errno中。

參數(shù)to指定了數(shù)據(jù)報將要發(fā)送到的地址。參數(shù)to必須指向一個可用的套接口地址,而參數(shù)tolen應(yīng)包含地址的正確長度。我們已經(jīng)在前面的章節(jié)中成為格式化地址的專家,所以在這里我們應(yīng)感到得心應(yīng)手了。

flags可用值如下所示,當(dāng)然在大多數(shù)時候我們都將其指定為0。
標(biāo)志            十六進(jìn)制    意義
0            0x0000        普通-沒有選項
MSG_OOB            0x0001        處理超過邊界的數(shù)據(jù)
MSG_DONTROUTE        0x0004        旁路路由,使用直接接口
MSG_DONTWAIT        0x0040        不緩沖,直接寫入
MSG_NOSIGNAL        0x4000        當(dāng)端點斷開時不發(fā)送信號

簡介recvfrom(2)函數(shù)
與sendto函數(shù)相對就是recvfrom函數(shù)。這個函數(shù)與read函數(shù)的不同就在于他允許我們同時指定我們要接收的數(shù)據(jù)報的發(fā)送者的地址。函數(shù)概要如下:
#include <sys/types.h>
#include <sys/socket.h>
int recvfrom(int s,
    void *buf,
    int len,
    unsigned flags,
    struct sockaddr *from,
    int *fromlen);
參數(shù)描述如下:
1 套接口s指定要從中接收數(shù)據(jù)報的套接口
2 buf指向開始接收數(shù)據(jù)報的緩沖區(qū)
3 最大長度len指定了buf的長度
4 選項標(biāo)志flags
5 指向接收套接口的地址緩沖,這將會接收發(fā)送者的地址
6 指向最大長度fromlen的指針

與正常的read函數(shù)相類似,接收緩沖區(qū)buf必須足夠大來接收數(shù)據(jù)報。最大長度是通過len指定的。

如果函數(shù)執(zhí)行失敗則會返回-1,而錯誤號則會存放在errno中。否則,函數(shù)將會返回實際接收到的字節(jié)數(shù)。這將是我們接收到的數(shù)據(jù)報的尺寸。

然而在這里要特別注意的是,最后一個參數(shù)是一個指向接收地址結(jié)構(gòu)長度的指針。指針?biāo)赶虻膇nt值必須包含接收地址結(jié)構(gòu)from的最大字節(jié)尺寸。一旦由函數(shù)返回,返回的地址尺寸放置在int變量中。實際上,由fromlen所指向的值同時作為輸入值和返回值。

可用的標(biāo)志值如下所示:
標(biāo)志        十六進(jìn)制    意義
0        0x0000        普通
MSG_OOB        0x0001        處理超過邊界的數(shù)據(jù)
MSG_PEEK    0x0002        讀取一個數(shù)據(jù)報而不從內(nèi)核接收隊列中刪除
MSG_WAITALL    0x0100        請求操作塊直到全部請求都已滿足
MSG_ERRQUEUE    0x2000        從錯誤隊列獲取一個數(shù)據(jù)包
MSG_NOSIGNAL    0x4000        當(dāng)另一端斷開連接時為流套接口關(guān)閉信號

編寫一個UDP數(shù)據(jù)報服務(wù)器

現(xiàn) 在我們已經(jīng)有足夠的知識來編寫一個數(shù)據(jù)報客戶端與服務(wù)器程序了。在這一部分,我們將從編寫一個數(shù)據(jù)報服務(wù)的例子開始。這個程序接受一個strftime (3)格式字符串作為輸入并且返回格式化的日期與時間字符串作為響應(yīng)。在這一節(jié)我們將會編寫一個數(shù)據(jù)報服務(wù)器,他可以獨立運行,接受格式化字符串作為輸入 數(shù)據(jù)報。在服務(wù)器使用strftime函數(shù)格式化日期字符串之后,他會將結(jié)果返回給客戶端程序。
/*
 * dgramsrvr.c
 *
 * Example datagram server:
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

/*
 * this function reports the error and
 * exits back to the shell:
 */
static void bail(const char *on_what)
{
    fputs(strerror(errno),stderr);
    fputs(": ",stderr);
    fputs(on_what,stderr);
    fputs("\n",stderr);
    exit(1);
}

int main(int argc,char **argv)
{
    int z;
    char *srvr_addr = NULL;
    struct sockaddr_in adr_inet;    /* AF_INET */
    struct sockaddr_in adr_clnt;    /* AF_INET */
    int len_inet;    /* length */
    int s;        /* socket */
    char dgram[512];    /* recv buffer */
    char dtfmt[512];    /* date/time result */
    time_t td;        /* current time and date */
    struct tm tm;    /* date time values */

    /*
     * use a server address from the command
     * line,if one has been provided.
     * otherwise,this program will default
     * to using the arbitrary address
     * 127.0.0.23:
     */
    if(argc >=2)
    {
    /* addr on cmdline */
    srvr_addr = argv[1];
    }
    else
    {
    /* use default address */
    srvr_addr = "127.0.0.23";
    }

    /*
     * create a UDP socket to use:
     */
    s = socket(AF_INET,SOCK_DGRAM,0);
    if(s==-1)
    bail("socket()");

    /*
     * create a socket address,for use
     * with bind:
     */
    memset(&adr_inet,0,sizeof adr_inet);
    adr_inet.sin_family = AF_INET;
    adr_inet.sin_port = htons(9090);
    adr_inet.sin_addr.s_addr = inet_addr(srvr_addr);

    if(adr_inet.sin_addr.s_addr == INADDR_NONE)
    bail("bad address");

    len_inet = sizeof adr_inet;

    /*
     * bind a address to our socket,so that
     * client program can contact this
     * server:
     */
    z = bind(s,(struct sockaddr *)&adr_inet,len_inet);
    if(z==-1)
    bail("bind()");

    /*
     * now wait for requests:
     */
    for(;;)
    {
    /*
     * block until the program receives a
     * datagram at our address an port:
     */
    len_inet = sizeof adr_clnt;
    z = recvfrom(s,        /* socket */
        dgram,        /* receiveing buffer */
        sizeof dgram,    /* max recv buf size */
        0,        /* flags */
        (struct sockaddr *)&adr_clnt,    /* addr */
        &len_inet);    /* addr len,in & out */
    if(z<0)
        bail("recvfrom()");

    /*
     * process the request:
     */
    if(!strcasecmp(dgram,"QUIT"))
        break;    /* quit server */

    /*
     * get the current date and time:
     */
    time(&td);    /* get current time & date */
    tm = *localtime(&td);    /* get componets */

    /*
     * formate a new date and time string,
     * based upon the input formate string:
     */
    strftime(dtfmt,        /* formatted result */
        sizeof dtfmt,    /* max result size */
        dgram,        /* input date/time format */
        &tm);        /* input date/time values */
    /*
     * send the formatted result back to the
     * client program:
     */
    z = sendto(s,        /* socket to send result */
        dtfmt,        /* the datagram result to send */
        strlen(dtfmt),    /* the datagram length */
        0,        /* flags */
        (struct sockaddr *)&adr_clnt,    /* addr */
        len_inet);    /* client address length */

    if(z<0)
        bail("sendto()");

    }

    /*
     * close the socket and exit:
     */
    close(s);
    return 0;
}

編寫一個UDP數(shù)據(jù)報客戶端

為了使用我們在前面所編寫的數(shù)據(jù)報服務(wù)程序,我們需要一個數(shù)據(jù)報客戶端程序。下面例子所提供的數(shù)據(jù)報客戶端可以提示我們?yōu)閟trftime輸入格式化文本。
/*
 * dgramclnt.c
 *
 * Example datagram client:
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

/*
 * This function reports the error and
 * exits back to the shell:
 */
static void bail(const char *on_what)
{
    fputs(strerror(errno),stderr);
    fputs(": ",stderr);
    fputs(on_what,stderr);
    fputs("\n",stderr);
    exit(1);
}

int main(int argc,char **argv)
{
    int z;
    int x;
    char *srvr_addr = NULL;
    struct sockaddr_in adr_srvr;    /* AF_INET */
    struct sockaddr_in adr;        /* AF_INET */
    int len_inet;            /* length */
    int s;                /* socket */
    char dgram[512];            /* recv buffer */
    
    /*
     * use a server address from the command
     * line,if one has been provided.
     * otherwise,this programe will default
     * to using the arbitrary address
     * 127.0.0.23:
     */
    if(argc>=2)
    {
    /* addr on command line: */
    srvr_addr = argv[1];
    }
    else
    {
    srvr_addr = "127.0.0.23";
    }

    /*
     * create a socket address,to use
     * to contact the server with:
     */
    memset(&adr_srvr,0,sizeof adr_srvr);
    adr_srvr.sin_family = AF_INET;
    adr_srvr.sin_port = htons(9090);
    adr_srvr.sin_addr.s_addr = inet_addr(srvr_addr);

    if(adr_srvr.sin_addr.s_addr == INADDR_NONE)
    bail("bad address");

    len_inet = sizeof adr_srvr;

    /*
     * create a UDP socket to use:
     */
    s = socket(AF_INET,SOCK_DGRAM,0);
    if(s==-1)
    bail("socket()");

    for(;;)
    {
    /*
     * prompt user for a date formate string:
     */
    fputs("\nEnter format string: ",stdout);
    if(!fgets(dgram,sizeof dgram,stdin))
        break;    /* EOF */

    /*
     * send format string to server:
     */
    z = sendto(s,        /* socket to send result */
        dgram,        /* the datagram result to snd */
        strlen(dgram),    /* the datagram length */
        0,        /* flags */
        (struct sockaddr *)&adr_srvr,    /* addr */
        len_inet);    /* server address length */
    if(z<0)
        bail("sendto()");

    /*
     * test if we asked for a server shutdown:
     */
    if(!strncmp(dgram,"QUIT",strlen(dgram)-1))
        break;        /* yes,we quit too */

    /*
     * wait for a response:
     */
    x = sizeof adr;
    z = recvfrom(s,        /* socket */
        dgram,        /* receiving buffer */
        sizeof dgram,    /* max recv buf size */
        0,        /* flags */
        (struct sockaddr *)&adr, /* addr */
        &x);        /* addr len,in & out */
    if(z<0)
        bail("recvfrom()");
    dgram[z]=0;

    /*
     * report result
     */
    printf("Result from %s port %u:\n\t‘%s‘\n",
        inet_ntoa(adr.sin_addr),
        (unsigned)ntohs(adr.sin_port),
        dgram);
    }

    /*
     * close the socket and exit:
     */
    close(s);
    putchar(‘\n‘);
    return 0;
}

測試數(shù)據(jù)報客戶端與服務(wù)器

我們將會在這里進(jìn)行的第一個測試適用于任何人,而無論你是否有可用的網(wǎng)絡(luò)或是獨立的PC。唯一的關(guān)鍵點就是我們需要將TCP/IP支持編譯進(jìn)入內(nèi)核。如果我們正在運行一個獨立的發(fā)行版本,例如RH系列,那么我們已經(jīng)滿足這些條件了。

要執(zhí)行這個測試,我們需要執(zhí)行下面的步驟:
1 啟動服務(wù)器程序
2 啟動客戶端程序
3 輸入客戶程序輸入
4 輸入C-D來結(jié)束程序,或者是使用QUIT來退出程序。

啟動服務(wù)器程序的輸出結(jié)果如下:
$ ./dgramsrvr &
[1] 4405
$

字符&將服務(wù)器程序放置在后臺運行,這樣我們就可以繼續(xù)使用當(dāng)前的終端來運行客戶端程序。
服務(wù)器程序啟動運行以后,我們就可以使用客戶程序來進(jìn)行測試了。下面顯示如何啟動客戶程序并進(jìn)行測試:
$ ./dgramclnt
Enter format string: %D
Result from 127.0.0.23 port 9090 :
08/13/99‘
Enter format string: %A %D %H:%M:%S
Result from 127.0.0.23 port 9090 :
Friday 08/13/99 22:14:02‘
Enter format string: quit
[1]+  Done                ./dgramsrvr
$

測試無服務(wù)器的情況

下面的輸出顯示沒有服務(wù)器運行時運行客戶程序的輸出結(jié)果:
$ ./dgramclnt
Enter format string: %D
Connection refused: recvfrom(2)
$
在這里我們可以看到客戶程序可以運行,并且可以創(chuàng)建套接口,要求輸入。甚至sendto函數(shù)也報告執(zhí)行成功。這就進(jìn)一步確認(rèn)了數(shù)據(jù)報的發(fā)送只是確保發(fā)送成功,而不確保接收成功。
在這種情況下,程序很幸運的得到了錯誤號來表明錯誤原因 。錯誤標(biāo)識是通過recvfrom函數(shù)調(diào)用得到的。當(dāng)客戶與服務(wù)器程序獨立運行在一個大的網(wǎng)絡(luò)并有多個路由的情況下,也許就不會得到這個錯誤號。

在實際使用中,如果另一端并沒有監(jiān)聽,我們不能依賴于得到錯誤碼。因為這個原因,UDP程序通常包含定時器的使用,如果在一定的時間內(nèi)沒有收到響應(yīng)則認(rèn)為沒有建立連接。

使用其他的IP進(jìn)行測試

在 前一節(jié)我們談到可以在命令行指定IP地址。如果我們設(shè)置了我們自己的網(wǎng)絡(luò),我們可以試著在不同的主機(jī)上運行客戶端與服務(wù)器程序。在下一個例子中,服務(wù)器程 序運行在192.168.0.1的主機(jī)上,而客戶端程序運行192.168.0.2的主機(jī)上。服務(wù)器程序的啟動如下所示:
$ ./dgramsrvr 192.168.0.1 &
[1] 4416
$
服務(wù)器成功啟動以后,就可以在另一個主機(jī)上調(diào)用客戶端程序??蛻舳说妮敵鼋Y(jié)果如下所示:
$ ./dgramclnt 192.168.0.1
Enter format string: %D
Result from 192.168.0.1 port 9090 :
‘08/13/99‘
Enter format string: %A (%D)
Result from 192.168.0.1 port 9090 :
‘Friday (08/13/99)‘
Enter format string: QUIT
$

正如輸出結(jié)果所示,通過在命令行指定地址通知客戶程序服務(wù)器位于192.168.0.1主機(jī)上。在試驗了兩個例子以后,輸入QUIT命令退出。

盡管這兩個例子可以正確的運行,但是我們要認(rèn)識到UDP是不可靠的。如果客戶端程序不能由服務(wù)器得到響應(yīng),程序就會掛起。如果我們正在編寫一個產(chǎn)品模型程序,我們需要提供超時代碼。當(dāng)原始的或是響應(yīng)的數(shù)據(jù)報丟失時,這會允許程序修復(fù)響應(yīng)缺失。

在客戶程序中保留bind

一些讀者也許已經(jīng)注意到在上個例子中所創(chuàng)建的套接口的客戶端程序并沒有調(diào)用bind函數(shù)。如果bind函數(shù)調(diào)用可以消除,那么為什么還要使用呢?

我 們也許還記得在前面的章節(jié)中,“綁定地址到套接口”,其中有一節(jié)名為“接口與地址”的部分,在其中解釋了bind函數(shù)可用于限制用于執(zhí)行通信的接口。其中 的例子是一個防火墻程序,他只與信任的內(nèi)部網(wǎng)絡(luò)進(jìn)行通信。如果現(xiàn)在這些對于我們來說有一些茫然,那么我們需要返回來查閱bind的相關(guān)內(nèi)容。

而 在我們的數(shù)據(jù)報例子程序中,bind函數(shù)調(diào)用被忽略了。這對于發(fā)送套接口有什么影響呢?正如我們在第五章所了解的,這實際表明這個程序可以接受任何流出的 接口,這正是數(shù)據(jù)報到其目的地的路由所要求的。實際上,套接口據(jù)說有一個寬套接口地址。然后,當(dāng)這個程序等待響應(yīng)時,他也會接受從任何流入接口得到的輸入 數(shù)據(jù)報。在這里也要注意套接口端口號也是寬的。在這個特定的程序中,任何客戶端口號都是可以接受的。

我們可以顯式的使用bind函數(shù)來請 求一個寬地址和端口。可以使用寬地址INADDR_NONE來完成這個工作。要請求一個寬端口號,可以將端口號指定為0。通過組合IP地址的 INADDR_NONE和0端口號,我們已經(jīng)請求bind顯式的為我們提供一個寬地址,而這是與我們沒有使用bind調(diào)用而得到的寬地址相同。

回應(yīng)寬地址

如果客戶程序的地址和端口號是寬的,我們也許想要知道服務(wù)器如何與回應(yīng)特定的端口。畢竟,我們應(yīng)如何縮寫向沒有一個特定IP地址和UDP端口號的客戶程序返回響應(yīng)?

問 題的答案就在于:實際上當(dāng)數(shù)據(jù)報發(fā)送時,就同時賦值了IP地址和端口號。我們前面的例子運行在IP地址為192.168.0.2的主機(jī)上。當(dāng)客戶程序調(diào)用 sendto函數(shù)時,數(shù)據(jù)報知道將會發(fā)送到IP地址為192.168.0.1的主機(jī)上。路由表指明IP地址為192.168.0.2的以太網(wǎng)卡將會用于發(fā) 送數(shù)據(jù)報。相應(yīng)的,發(fā)送的數(shù)據(jù)報有一個192.168.0.2的from地址。這是在服務(wù)器端看到的地址。然而,端口號是寬的,并且將會所選擇的IP地址 選擇任意的自由端口號。

如果另一個數(shù)據(jù)報發(fā)送到一個不同的網(wǎng)絡(luò),那么from IP地址將會是不同的值。from地址返應(yīng)了用于發(fā)送數(shù)據(jù)報的網(wǎng)絡(luò)接口的IP地址。

這是需要理解的一個重要概念,而且也許對于初學(xué)者來說也是最難理解的事情。如果我們現(xiàn)在并不能完全的理解這些內(nèi)容,那么我們就需要回顧一下第五章的內(nèi)容。作為練習(xí),我們可以在我們的例子程序中加入下面的printf語句:
printf("Client from %s port %u;\n",
    inet_ntoa(adr_clnt.sin_addr),
        (unsigned)ntohs(adr_clnt.sin_port));

編譯以后重新運行程序,程序的運行結(jié)果如下:
$ ./dgramsrvr &
[1] 733
$ ./dgramclnt
Enter format string: %D
Client from 127.0.0.23 port 1027;
Result from 127.0.0.23 port 9090 :
          ‘08/15/99‘
      Enter format string: %A %D
      Client from 127.0.0.23 port 1027;
      Result from 127.0.0.23 port 9090 :
      ‘Sunday 08/15/99‘
Enter format string: QUIT
Client from 127.0.0.23 port 1027;
[1]+ Done               ./dgramsrvr
$
在這里我們可以注意到所有的數(shù)據(jù)報發(fā)送到服務(wù)器,而數(shù)據(jù)報的from地址報告如下:
Client from 127.0.0.23 port 1027;
這再一次驗證了當(dāng)在客戶端發(fā)送套接口沒有使用bind函數(shù)時,會依據(jù)需求賦值合適的IP地址和最終的端口號。

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1676892


[收藏到我的網(wǎng)摘]   mylxiaoyi發(fā)表于 2007年07月03日 16:20:00

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多