国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 學(xué)院 > 操作系統(tǒng) > 正文

網(wǎng)絡(luò)IPC:套接字之?dāng)?shù)據(jù)傳輸

2024-06-28 13:27:50
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友
網(wǎng)絡(luò)ipC:套接字之?dāng)?shù)據(jù)傳輸

既然將套接字端點(diǎn)表示為文件描述符,那么只要建立連接,就可以使用read和write來(lái)通過(guò)套接字通信。通過(guò)在connect函數(shù)里設(shè)置對(duì)方地址,數(shù)據(jù)報(bào)套接字也可以“連接”。在套接字描述符上采用read和write是非常有意義的,因?yàn)榭梢詡鬟f套接字描述符到那些原先設(shè)計(jì)為處理本地文件的函數(shù)。而且可以安排傳遞套接字描述符到執(zhí)行程序的子進(jìn)程,該子進(jìn)程并不了解套接字。

盡管可以通過(guò)read和write交換數(shù)據(jù),但這就是這兩個(gè)函數(shù)所能做的一切。如果想指定選項(xiàng)、從多個(gè)客戶端接收數(shù)據(jù)包或者發(fā)送帶外數(shù)據(jù),需要采用6個(gè)傳遞數(shù)據(jù)的套接字函數(shù)中的一個(gè)。

三個(gè)函數(shù)用來(lái)發(fā)送數(shù)據(jù),三個(gè)用來(lái)接收數(shù)據(jù)。首先,考察用于發(fā)送數(shù)據(jù)的函數(shù)

最簡(jiǎn)單的是send,它和write很像,但是可以指定標(biāo)志來(lái)改變處理傳輸數(shù)據(jù)的方式。

#include <sys/socket.h>ssize_t send(int sockfd, const void *buf, size_t nbytes, int flags);返回值:若成功則返回發(fā)送的字節(jié)數(shù),若出錯(cuò)則返回-1

類似write,使用send時(shí)套接字必須已經(jīng)連接。參數(shù)buf和nbytes與write中的含義一致。

然而,與write不同的是,send支持第四個(gè)參數(shù)flags。其中3個(gè)標(biāo)志是Single UNIX Specification規(guī)定的,但是其他標(biāo)志通常實(shí)現(xiàn)也支持。表16-7總結(jié)了這些標(biāo)志。

表16-7 send套接字調(diào)用標(biāo)志

未命名

如果send成功返回,并不必然表示連接另一端的進(jìn)程接收數(shù)據(jù)。所保證的僅是當(dāng)send成功返回時(shí),數(shù)據(jù)已經(jīng)無(wú)錯(cuò)誤地發(fā)送到網(wǎng)絡(luò)上。

對(duì)于支持為報(bào)文設(shè)限的協(xié)議,如果單個(gè)報(bào)文超過(guò)協(xié)議所支持的最大尺寸,send失敗并將errno設(shè)置為EMSGSIZE;對(duì)于字節(jié)流協(xié)議,send會(huì)阻塞直到整個(gè)數(shù)據(jù)被傳輸

函數(shù)sendto和send很類似。區(qū)別在于sendto允許在無(wú)連接的套接字上指定一個(gè)目標(biāo)地址。

#include <sys/socket.h>ssize_t sendto(int sockfd, const void *buf, size_t nbytes, int flags,               const struct sockaddr *destaddr, socklen_t destlen);返回值:若成功則返回發(fā)送的字節(jié)數(shù),若出錯(cuò)則返回-1

對(duì)于面向連接的套接字,目標(biāo)地址是忽略的,因?yàn)槟繕?biāo)地址蘊(yùn)含在連接中。對(duì)于無(wú)連接的套接字,不能使用send,除非在調(diào)用connect時(shí)預(yù)先設(shè)定了目標(biāo)地址,或者采用sendto來(lái)提供另外一種發(fā)送報(bào)文方式。

可以使用不止一個(gè)的選擇來(lái)通過(guò)套接字發(fā)送數(shù)據(jù)。可以調(diào)用帶有msghdr結(jié)構(gòu)的sendmsg來(lái)指定多重緩沖區(qū)傳輸數(shù)據(jù),這和writev很相像(http://www.CUOXin.com/nufangrensheng/p/3559304.html)。

#include <sys/socket.h>ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);返回值:若成功則返回發(fā)送的字節(jié)數(shù),出錯(cuò)則返回-1

POSIX.1定義了msghdr結(jié)構(gòu),它至少應(yīng)該有如下成員:

struct msghdr {    void            *msg_name;         /* optional address */    socklen_t        msg_namelen;      /* address size in bytes */    struct iovec    *msg_iov;          /* array of I/O buffers */    int              msg_iovlen;       /* number of elements in array */    void            *msg_control;      /* ancillary data */    socklen_t        msg_controllen;   /* number of ancillary bytes */    int              msg_flags;        /* flags for received message */    ...};

iovec結(jié)構(gòu)可參考http://www.CUOXin.com/nufangrensheng/p/3559304.html。

下面是用于接收數(shù)據(jù)的函數(shù)。

函數(shù)recv和read很像,但是允許指定選項(xiàng)來(lái)控制如何接收數(shù)據(jù)。

#include <sys/socket.h>ssize_t recv(int sockfd, void *buf, size_t nbytes, int flags);返回值:以字節(jié)計(jì)數(shù)的消息長(zhǎng)度,若無(wú)可用消息或?qū)Ψ揭呀?jīng)按序結(jié)束則返回0,出錯(cuò)則返回-1

表16-8總結(jié)了flags標(biāo)志。其中只有三個(gè)標(biāo)志是Single UNIX Specification規(guī)定的。

表16-8 recv套接字調(diào)用標(biāo)志

未命名

當(dāng)指定MSG_PEEK標(biāo)志時(shí),可以查看下一個(gè)要讀的數(shù)據(jù)但不會(huì)真正取走。當(dāng)再次調(diào)用read或recv函數(shù)時(shí)會(huì)返回剛才查看的數(shù)據(jù)。

對(duì)于SOCK_STREAM套接字,接收的數(shù)據(jù)可以比請(qǐng)求少。標(biāo)志MSG_WAITALL阻止這種行為,除非所需數(shù)據(jù)全部收到,recv才會(huì)返回。對(duì)于SOCK_DGRAM和SOCK_SEQPACKET套接字,MSG_WAITALL標(biāo)志沒(méi)有改變什么行為,因?yàn)檫@些基于報(bào)文的套接字類型一次讀取就返回整個(gè)報(bào)文。

如果發(fā)送者已經(jīng)調(diào)用shutdown(http://www.CUOXin.com/nufangrensheng/p/3564695.html)來(lái)結(jié)束傳輸,或者網(wǎng)絡(luò)協(xié)議支持默認(rèn)的順序關(guān)閉并且發(fā)送端已經(jīng)關(guān)閉,那么當(dāng)所有數(shù)據(jù)接收完畢后,recv返回0。

如果有興趣定位發(fā)送者,可以使用recvfrom來(lái)得到數(shù)據(jù)發(fā)送者的源地址。

#include <sys/socket.h>ssize_t recvfrom(int sockfd, void *restrict buf, size_t len, int flags,                 struct sockaddr *restrict addr,                 socklen_t *restrict addrlen);返回值:以字節(jié)計(jì)數(shù)的消息長(zhǎng)度,若無(wú)可用消息或?qū)Ψ揭呀?jīng)按序結(jié)束則返回0,若出錯(cuò)則返回-1

如果addr非空,它將包含數(shù)據(jù)發(fā)送者的套接字端點(diǎn)地址。當(dāng)調(diào)用recvfrom時(shí),需要設(shè)置addrlen參數(shù)指向一個(gè)包含addr所指的套接字緩沖區(qū)字節(jié)大小的整數(shù)。返回時(shí),該整數(shù)設(shè)為該地址的實(shí)際大小。

因?yàn)榭梢垣@得發(fā)送者的實(shí)際地址,recvfrom通常用于無(wú)連接套接字。否則recvfrom等同于recv。

為了將接收到的數(shù)據(jù)送入多個(gè)緩沖區(qū)(類似于readv(http://www.CUOXin.com/nufangrensheng/p/3559304.html)),或者想接收輔助數(shù)據(jù),可以使用recvmsg

#include <sys/socket.h>ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);返回值:以字節(jié)計(jì)數(shù)的消息長(zhǎng)度,若無(wú)可用消息或?qū)Ψ揭呀?jīng)按序結(jié)束則返回0,若出錯(cuò)則返回-1

結(jié)構(gòu)msghdr(在sendmsg中見(jiàn)過(guò))被recvmsg用于指定接收數(shù)據(jù)的輸入緩沖區(qū)。可以設(shè)置參數(shù)flags來(lái)改變r(jià)ecvmsg的默認(rèn)行為。返回時(shí),msghdr結(jié)構(gòu)中的msg_flags字段被設(shè)為所接收數(shù)據(jù)的各種特征(進(jìn)入recvmsg時(shí)msg_flags被忽略)。從recvmsg中返回的各種可能值總結(jié)在表16-9中。

表16-9 從recvmsg中返回的msg_flags標(biāo)志

未命名

實(shí)例:面向連接的客戶端

程序清單16-4顯示了一個(gè)客戶端命令,該命令用于與服務(wù)器通信以獲得系統(tǒng)命令uptime的輸出。該服務(wù)成為“remote uptime”(簡(jiǎn)稱為“ruptime”)。

程序清單16-4 用于獲取服務(wù)器uptime的客戶端命令

#include "apue.h"#include <netdb.h>#include <errno.h>#include <sys/socket.h>#define MAXADDRLEN    256#define    BUFLEN        128    extern int connect_retry(int, const struct sockaddr *, socklen_t);void PRint_uptime(int sockfd){    int    n;    char     buf[BUFLEN];        while(( n = recv(sockfd, buf, BUFLEN, 0)) > 0)        write(STDOUT_FILENO, buf, n);    if(n < 0)        err_sys("recv error");}int main(int argc, char *argv[]){    struct addrinfo *ailist, *aip;    struct addrinfo hint;    int        sockfd, err;    if(argc != 2)        err_quit("usage: ruptime hostname");    hint.ai_flags = 0;    hint.ai_family = 0;    hint.ai_socktype = SOCK_STREAM;    hint.ai_protocol = 0;    hint.ai_addrlen = 0;    hint.ai_canonname = NULL;    hint.ai_addr = NULL;    hint.ai_next = NULL;    if((err = getaddrinfo(argv[1], "ruptime", &hint, &ailist)) != 0)        err_quit("getaddrinfo error: %s", gai_strerror(err));    for(aip = ailist; aip != NULL; aip = aip->ai_next)    {        if((sockfd = socket(aip->ai_family, SOCK_STREAM, 0)) < 0)            err = errno;        if(connect_retry(sockfd, aip->ai_addr, aip->ai_addrlen) < 0)        {            err = errno;        }        else        {            print_uptime(sockfd);            exit(0);        }    }    fprintf(stderr, "can't connect to %s: %s/n", argv[1], strerror(err));    exit(1);}

其中,connect_retry函數(shù)見(jiàn):http://www.CUOXin.com/nufangrensheng/p/3565858.html中的程序清單16-2

這個(gè)程序連接服務(wù)器,讀取服務(wù)器發(fā)送過(guò)來(lái)的字符串并將其打印到標(biāo)準(zhǔn)輸出。既然使用SOCK_STREAM套接字,就不能保證在一次recv調(diào)用中會(huì)讀取整個(gè)字符串,所以需要重復(fù)調(diào)用直到返回0。

如果服務(wù)器支持多重網(wǎng)絡(luò)接口或多重網(wǎng)絡(luò)協(xié)議,函數(shù)getaddrinfo會(huì)返回不止一個(gè)候選地址。輪流嘗試每個(gè)地址,當(dāng)找到一個(gè)允許連接到服務(wù)的地址時(shí)便可停止。

編譯上面的程序成功后,執(zhí)行時(shí)出現(xiàn)錯(cuò)誤:getaddrinfo error:Servname not supported for ai_socktype,后來(lái)經(jīng)查詢?cè)趆ttp://blog.163.com/yjie_life/blog/static/16319833720110311528528/找到了解決辦法。其原因是我們?cè)趃etaddrinfo第二個(gè)參數(shù)傳入的服務(wù)名“ruptime”還沒(méi)有分配端口號(hào),我們可以手動(dòng)為其添加端口號(hào),只需在/etc/services文件中添加一行:ruptime 8888/tcp 其中8888是分配的端口號(hào),需要大于1024且不與其他服務(wù)的端口號(hào)重復(fù)就行,后面的tcp是協(xié)議。

實(shí)例:面向連接的服務(wù)器

程序清單16-5顯示服務(wù)器程序,用來(lái)提供uptime命令到程序清單16-4的客戶端程序的輸出

程序清單16-5 提供系統(tǒng)uptime的服務(wù)器程序

#include "apue.h"#include <netdb.h>#include <errno.h>#include <syslog.h>#include <sys/socket.h>#define BUFLEN    128#define QLEN    10#ifndef HOST_NAME_MAX#define HOST_NAME_MAX    256#endifextern int initserver(int, struct sockaddr *, socklen_t, int);voidserve(int sockfd){    int      clfd;    FILE    *fp;    char     buf[BUFLEN];    for(;;)    {        clfd = accept(sockfd, NULL, NULL);        if(clfd < 0)        {            syslog(LOG_ERR, "ruptimed: accept error: %s", strerror(errno));            exit(1);        }        if((fp = popen("/usr/bin/uptime", "r")) == NULL)        {            sprintf(buf, "error: %s/n", strerror(errno));            send(clfd, buf, strlen(buf), 0);        }        else        {            while(fgets(buf, BUFLEN, fp) != NULL)                send(clfd, buf, strlen(buf), 0);            pclose(fp);        }        close(clfd);    }}intmain(int argc, char *argv[]){    struct addrinfo *ailist, *aip;    struct addrinfo hint;    int        sockfd, err, n;    char        *host;            if(argc != 1)        err_quit("usage: ruptimed");#ifdef _SC_HOST_NAME_MAX    n = sysconf(_SC_HOST_NAME_MAX);    if(n < 0)    /* best guess */#endif        n = HOST_NAME_MAX;    host = malloc(n);    if(host == NULL)        err_sys("malloc error");    if(gethostname(host, n) < 0)        err_sys("gethostname error");    daemonize("ruptimed");    hint.ai_flags = AI_CANONNAME;    hint.ai_family = 0;    hint.ai_socktype = SOCK_STREAM;    hint.ai_protocol = 0;    hint.ai_addrlen = 0;    hint.ai_canonname = NULL;    hint.ai_addr = NULL;    hint.ai_next = NULL;    if((err = getaddrinfo(host, "ruptime", &hint, &ailist)) != 0)    {        syslog(LOG_ERR, "ruptimed: getaddrinfo error: %s", gai_strerror(err));        exit(1);    }    for(aip = ailist; aip != NULL; aip = aip->ai_next)    {        if((sockfd = initserver(SOCK_STREAM, aip->ai_addr, aip->ai_addrlen, QLEN)) >= 0)        {            serve(sockfd);            exit(0);        }    }    exit(1);}

其中,

initserver函數(shù)見(jiàn):http://www.CUOXin.com/nufangrensheng/p/3565858.html中的程序清單16-3

daemonize函數(shù)見(jiàn):http://www.CUOXin.com/nufangrensheng/p/3544104.html中的程序清單13-1

為了找到地址,服務(wù)器程序需要獲得其運(yùn)行時(shí)的主機(jī)名字。一些系統(tǒng)不定義_SC_HOST_NAME_MAX常量,因此這種情況下使用HOST_NAME_MAX。如果系統(tǒng)不定義HOST_NAME_MAX就自己定義。POSXI.1規(guī)定該值的最小值為255字節(jié),不包括終結(jié)符,因此定義HOST_NAME_MAX為256以包括終結(jié)符。

通過(guò)調(diào)用gethostname,服務(wù)器程序獲得主機(jī)名字。并查看遠(yuǎn)程uptime服務(wù)(ruptime)地址。可能會(huì)有多個(gè)地址返回,但簡(jiǎn)單地選擇第一個(gè)來(lái)建立被動(dòng)套接字端點(diǎn),在這個(gè)端點(diǎn)等待到來(lái)的連接請(qǐng)求。

實(shí)例:另一個(gè)面向連接的服務(wù)器

前面說(shuō)過(guò)采用文件描述符來(lái)訪問(wèn)套接字是非常有意義的,因?yàn)樵试S程序?qū)β?lián)網(wǎng)環(huán)境的網(wǎng)絡(luò)訪問(wèn)一無(wú)所知。程序清單16-6中顯示的服務(wù)器程序版本顯示了這一點(diǎn)。為了代替從uptime命令中讀取輸出并發(fā)送到客戶端,服務(wù)器安排uptime命令的標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)出錯(cuò)替換為連接到客戶端的套接字端點(diǎn)。

程序清單16-6 用于顯示命令直接寫(xiě)到套接字的服務(wù)器程序

#include "apue.h"#include <netdb.h>#include <errno.h>#include <syslog.h>#include <fcntl.h>#include <sys/socket.h>#include <sys/wait.h>#define QLEN    10#ifndef HOST_NAME_MAX#define HOST_NAME_MAX 256#endifextern int initserver(int, struct sockaddr *, socklen_t, int);voidserve(int sockfd){    int      clfd, status;    pid_t    pid;        for(;;)    {        clfd = accept(sockfd, NULL, NULL);        if(clfd < 0)        {            syslog(LOG_ERR, "ruptimed: accept error: %s",                strerror(errno));            exit(1);        }        if((pid = fork()) < 0)        {            syslog(LOG_ERR, "ruptimed: fork error: %s",                 strerror(errno));            exit(1);        }        else if(pid == 0)    /* child */        {            /*            * The parent called daemonize, so             * STDIN_FILENO, STDOUT_FILENO, and STDERR_FILENO            * are already open to /dev/null. Thus, the call to            * close doesn't need to be protected by checks that            * clfd isn't already equal to one of these values.            */            if(dup2(clfd, STDOUT_FILENO) != STDOUT_FILENO ||                dup2(clfd, STDERR_FILENO) != STDERR_FILENO)            {                syslog(LOG_ERR, "ruptimed: unexpected error");                exit(1);            }            close(clfd);            execl("/usr/bin/uptime", "uptime", (char *)0);            syslog(LOG_ERR, "ruptimed: unexpected return from exec:                 %s", strerror(errno));        }        else    /* parent */        {            close(clfd);            waitpid(pid, &status, 0);        }        }}intmain(int argc, char *argv[]){    struct addrinfo *ailist, *aip;    struct addrinfo  hint;    int              sockfd, err, n;    char            *host;    if(argc != 1)        err_quit("usage: ruptimed");#ifdef _SC_HOST_NAME_MAX    n = sysconf(_SC_HOST_NAME_MAX);    if(n < 0)    /* best guess */#endif        n = HOST_NAME_MAX;    host = malloc(n);    if(host == NULL)        err_sys("malloc error");    if(gethostname(host, n) < 0)        err_sys("gethostname error");    daemonize("ruptimed");    hint.ai_flags = AI_CANONNAME;    hint.ai_family = 0;    hint.ai_socktype = SOCK_STREAM;    hint.ai_protocol = 0;    hint.ai_addrlen = 0;    hint.ai_canonname = NULL;    hint.ai_addr = NULL;    hint.ai_next = NULL;    if((err = getaddrinfo(host, "ruptime", &hint, &ailist)) != 0)    {        syslog(LOG_ERR, "ruptimed: getaddrinfo error: %s",             gai_strerror(err));        exit(1);    }    for(aip = ailist; aip != NULL; aip = aip->ai_next)    {        if((sockfd = initserver(SOCK_STREAM, aip->ai_addr,             aip->ai_addrlen, QLEN)) >= 0)        {            serve(sockfd);            exit(0);        }    }    exit(1);}

以前的方式是采用popen來(lái)運(yùn)行uptime命令,并從連接到命令標(biāo)準(zhǔn)輸出的管道讀取輸出,現(xiàn)在采用fork來(lái)創(chuàng)建一個(gè)子進(jìn)程,并使用dup2使子進(jìn)程的STDIN_FILENO的副本打開(kāi)到/dev/null、STDOUT_FILENO和STDERR_FILENO打開(kāi)到套接字端點(diǎn)。當(dāng)執(zhí)行uptime時(shí),命令將結(jié)果寫(xiě)到標(biāo)準(zhǔn)輸出,該標(biāo)準(zhǔn)輸出連到套接字,所以數(shù)據(jù)被送到ruptime客戶端命令。

父進(jìn)程可以安全地關(guān)閉連接到客戶端的文件描述符,因?yàn)樽舆M(jìn)程仍舊打開(kāi)著。父進(jìn)程等待子進(jìn)程處理完畢,所以子進(jìn)程不會(huì)變成僵死進(jìn)程。既然運(yùn)行uptime花費(fèi)時(shí)間不會(huì)太長(zhǎng),父進(jìn)程在接受下一個(gè)連接請(qǐng)求之前,可以等待子進(jìn)程退出。不過(guò),這種策略不適合子進(jìn)程運(yùn)行時(shí)間比較長(zhǎng)的情況。

前面的例子采用面向連接的套接字。但如何選擇合適的套接字類型?何時(shí)采用面向連接的套接字,何時(shí)采用無(wú)連接的套接字呢?答案取決于要做的工作以及對(duì)錯(cuò)誤的容忍程度。

對(duì)于無(wú)連接的套接字,數(shù)據(jù)包的到來(lái)可能已經(jīng)沒(méi)有次序,因此當(dāng)所有的數(shù)據(jù)不能放在一個(gè)包里時(shí),在應(yīng)用程序里必須關(guān)心包的次序。包的最大尺寸是通信協(xié)議的特性。并且對(duì)于無(wú)連接套接字,包可能丟失。如果應(yīng)用程序不能容忍這種丟失,必須使用面向連接的套接字。

容忍包丟失意味著兩個(gè)選擇。如果想和對(duì)方可靠通信,必須對(duì)數(shù)據(jù)報(bào)編號(hào),如果發(fā)現(xiàn)包丟失,則要求對(duì)方重新傳輸。既然包可能因延遲而疑似丟失,我們要求重傳,但該包卻又出現(xiàn),與重傳過(guò)來(lái)的包重復(fù)。因此必須識(shí)別重復(fù)包,如果出現(xiàn)重復(fù)包,則將其丟棄。

另外一個(gè)選擇是通過(guò)讓用戶再次嘗試命令來(lái)處理錯(cuò)誤。對(duì)于簡(jiǎn)單的應(yīng)用程序,這就足夠;但對(duì)于復(fù)雜的應(yīng)用程序,這種處理方式通常不是可行的選擇,一般在這種情況下使用面向連接的套接字更為可取。

面向連接的套接字的缺陷在于需要更多的時(shí)間和工作來(lái)建立一個(gè)連接,并且每個(gè)連接需要從操作系統(tǒng)中消耗更多的資源。

實(shí)例:無(wú)連接的客戶端

程序清單16-7中的程序是采用數(shù)據(jù)報(bào)套接字接口的uptime客戶端命令版本。

程序清單16-7 采用數(shù)據(jù)報(bào)服務(wù)的客戶端命令

#include "apue.h"#include <netdb.h>#include <errno.h>#include <sys/socket.h>#define BUFLEN    128#define TIMEOUT    20void sigalrm(int signo){}void print_uptime(int sockfd, struct addrinfo *aip){    int    n;    char    buf[BUFLEN];        buf[0] = 0;    if(sendto(sockfd, buf, 1, 0, aip->ai_addr, aip->ai_addrlen) < 0)        err_sys("sendto error");    alarm(TIMEOUT);    if((n = recvfrom(sockfd, buf, BUFLEN, 0, NULL, NULL)) < 0)    {        if(errno != EINTR)            alarm(0);        err_sys("recv error");    }    alarm(0);    write(STDOUT_FILENO, buf, 0);}int main(int argc, char *argv[]){    struct addrinfo        *ailist, *aip;    struct addrinfo         hint;    int                     sockfd, err;    struct sigaction        sa;        if(argc != 2)        err_quit("usage: ruptime hostname");    sa.sa_handler = sigalrm;    sa.sa_flags = 0;    sigemptyset(&sa.sa_mask);    if(sigaction(SIGALRM, &sa, NULL) < 0)        err_sys("sigaction error");    hint.ai_flags = 0;    hint.ai_family = 0;    hint.ai_socktype = SOCK_DGRAM;    hint.ai_protocol = 0;    hint.ai_addrlen = 0;    hint.ai_canonname = NULL;    hint.ai_addr = NULL;    hint.ai_next = NULL;    if((err = getaddrinfo(argv[1], "ruptime", &hint, &ailist)) != 0)        err_quit("getaddrinfo error: %s", gai_strerror(err));    for(aip = ailist; aip != NULL; aip = aip->ai_next)    {        if((sockfd = socket(aip->ai_family, SOCK_DGRAM, 0)) < 0)        {            err = errno;        }        else        {            print_uptime(sockfd, aip);            exit(0);        }    }    fprintf(stderr, "can't contact %s: %s/n", argv[1], strerror(err));    exit(1);}

除了為SIGALRM增加了一個(gè)信號(hào)處理程序以外,基于數(shù)據(jù)報(bào)的客戶端main函數(shù)和面向連接的客戶端中的類似。使用alarm函數(shù)來(lái)避免調(diào)用recvfrom時(shí)無(wú)限期阻塞。

對(duì)于面向連接的協(xié)議,需要在交換數(shù)據(jù)前連接服務(wù)器。對(duì)于服務(wù)器來(lái)說(shuō),到來(lái)的連接請(qǐng)求已經(jīng)足夠判斷出所需提供給客戶端的服務(wù)。但是對(duì)于基于數(shù)據(jù)報(bào)的協(xié)議,需要有一種方法來(lái)通知服務(wù)器需要它提供服務(wù)。本例中,只是簡(jiǎn)單地給服務(wù)器發(fā)送1字節(jié)的消息。服務(wù)器接收后從包中得到地址,并使用這個(gè)地址來(lái)發(fā)送響應(yīng)消息。如果服務(wù)器提供多個(gè)服務(wù),可以使用這個(gè)請(qǐng)求消息來(lái)指示所需要的服務(wù),但既然服務(wù)器只做一件事情,1字節(jié)消息的內(nèi)容是無(wú)關(guān)緊要的。

如果服務(wù)器不在運(yùn)行狀態(tài),客戶端調(diào)用recvfrom便會(huì)無(wú)限期阻塞。對(duì)于面向連接的例子,如果服務(wù)器不運(yùn)行,connect調(diào)用會(huì)失敗。為了避免無(wú)限期阻塞,調(diào)用recvfrom之前設(shè)置警告時(shí)鐘。

實(shí)例:無(wú)連接服務(wù)器

程序清單16-8中的程序是數(shù)據(jù)報(bào)版本的uptime服務(wù)器程序。

程序清單16-8 基于數(shù)據(jù)報(bào)提供系統(tǒng)uptime的服務(wù)器程序

#include "apue.h"#include <netdb.h>#include <errno.h>#include <syslog.h>#include <sys/socket.h>#define BUFLEN        128#define    MAXADDRLEN    256#ifndef    HOST_NAME_MAX#define HOST_NAME_MAX    256#endifextern int initserver(int, struct sockaddr *, socklen_t, int);voidserve(int sockfd){    int          n;    socklen_t    alen;    FILE        *fp;        char         buf[BUFLEN];    char         abuf[MAXADDRLEN];    for(;;)    {        alen = MAXADDRLEN;        if((n = recvfrom(sockfd, buf, BUFLEN, 0,            (struct sockaddr *)abuf, &alen)) < 0)        {            syslog(LOG_ERR, "ruptimed: recvfrom error: %s",                 strerror(errno));            exit(1);        }        if((fp = popen("/usr/bin/uptime", "r")) == NULL)        {            sprintf(buf, "error: %s/n", strerror(errno));            sendto(sockfd, buf, strlen(buf), 0,                (struct sockaddr *)abuf, alen);        }        else        {            if(fgets(buf, BUFLEN, fp) != NULL)                sendto(sockfd, buf, strlen(buf), 0,                    (struct sockaddr *)abuf, alen);            pclose(fp);        }    }    }intmain(int argc, char *argv[]){    struct addrinfo *ailist, *aip;    struct addrinfo  hint;    int              sockfd, err, n;    char            *host;        if(argc != 1)    {        err_quit("usage: ruptimed");            }#ifdef _SC_HOST_NAME_MAX    n = sysconf(_SC_HOST_NAME_MAX);    if(n < 0)    /* best guess */#endif        n = HOST_NAME_MAX;    host = malloc(n);    if(host == NULL)        err_sys("malloc error");    if(gethostname(host, n) < 0)        err_sys("gethostname error");    daemonize("ruptimed");    hint.ai_flags = AI_CANONNAME;    hint.ai_family = 0;    hint.ai_socktype = SOCK_DGRAM;    hint.ai_protocol = 0;    hint.ai_addrlen = 0;    hint.ai_canonname = NULL;    hint.ai_addr = NULL;    hint.ai_next = NULL;    if((err = getaddrinfo(host, "ruptime", &hint, &ailist)) != 0)    {        syslog(LOG_ERR, "ruptimed: getaddrinfo error: %s",            gai_strerror(err));        exit(1);    }    for(aip = ailist; aip != NULL; aip = aip->ai_next)    {        if((sockfd = initserver(SOCK_DGRAM, aip->ai_addr,             aip->ai_addrlen, 0)) >= 0)        {            serve(sockfd);            exit(0);        }    }    exit(1);}

服務(wù)器在recvfrom中阻塞等待服務(wù)請(qǐng)求。當(dāng)一個(gè)請(qǐng)求到達(dá)時(shí),保存請(qǐng)求者地址并使用popen來(lái)運(yùn)行uptime命令。采用sendto函數(shù)將輸出發(fā)送到客戶端,其目標(biāo)地址就設(shè)為剛才的請(qǐng)求者地址。

本篇博文內(nèi)容摘自《UNIX環(huán)境高級(jí)編程》(第2版),僅作個(gè)人學(xué)習(xí)記錄所用。關(guān)于本書(shū)可參考:http://www.apuebook.com/


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 水富县| 罗江县| 凤冈县| 屯门区| 综艺| 红桥区| 德钦县| 山阳县| 阳江市| 大洼县| 孙吴县| 来凤县| 翁牛特旗| 慈利县| 吴忠市| 遵义县| 宜章县| 武宁县| 通山县| 牡丹江市| 江安县| 六盘水市| 枣阳市| 鹤岗市| 道真| 佛山市| 呼图壁县| 平度市| 罗定市| 玛曲县| 高唐县| 荣昌县| 楚雄市| 达孜县| 宜宾市| 青龙| 安新县| 江津市| 祁东县| 施秉县| 石景山区|