apue第16章习题16.3:支持多个端点的服务

来源:互联网 发布:o的故事 知乎 编辑:程序博客网 时间:2024/06/05 09:26
#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
#endif

fd_set    allset;
int     servers[FD_SETSIZE];
int     maxi = -1;

extern int initserver(int, struct sockaddr *, socklen_t, int);

void
serve(int maxfd)
{
    int    i = 0, clfd, sockfd, nready = 0;
    FILE    *fp;
    char    buf[BUFLEN];
    fd_set  readset;

    FD_ZERO(&readset);

    for (;;) {
        readset = allset;
        
        nready = select(maxfd + 1, &readset, NULL, NULL, NULL);
        if (nready < 0)
        {
            syslog(LOG_ERR, "ruptimed: select error: %s",
              strerror(errno));
            exit(1);
        }

        for (i = 0; i <= maxi; i++)
        {
            sockfd = servers[i];
            if (FD_ISSET(sockfd, &readset))
            {
                clfd = accept(sockfd, NULL, NULL);
                if (clfd < 0)

                    continue;

                break;

            }
        }


        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);
    }
}

int
main(int argc, char *argv[])
{
    struct addrinfo    *ailist, *aip;
    struct addrinfo    hint;
    int                sockfd, err, n;
    char            *host;
    char   *addr, abuf[INET_ADDRSTRLEN];
    struct sockaddr_in  *sinp;
    int    i = 0, maxfd = -1;

    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");

    for (i = 0; i < FD_SETSIZE; i++)
        servers[i] = -1;

    FD_ZERO(&allset);

    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
) {
            sinp = (struct sockaddr_in *)aip->ai_addr;
            addr = inet_ntop(AF_INET, &sinp->sin_addr, abuf, INET_ADDRSTRLEN);
            syslog(LOG_INFO, "canonname: %s, address: %s, port: %d", aip->ai_canonname,
                addr, ntohs(sinp->sin_port));
            
            for (i = 0; i < FD_SETSIZE; i++) {
                if (servers[i] < 0) {
                    servers[i] = sockfd;
                    
                    if (i > maxi)
                        maxi = i;
                    break;
                }                
            }

            if (maxfd < sockfd)
                maxfd = sockfd;

            FD_SET(sockfd, &allset)
;
        }
    }
    
    if (maxfd >= 0)    
        serve(maxfd);
    else
        exit(1);

    exit(0);

}

main函数中红色部分对getaddrinfo返回的addrinfo链表中的端点依次调用initserver使其处于监听状态,使用servers数组中的第一个未用项记录这个监听描述符,maxi记录servers数组当前使用的最大下标,maxfd记录每个监听描述符中的最大值(+1后为select的第一个参数)。最后将每个监听描述符加入描述符集合allset中。

serve函数中红色部分将readset复制为allset的副本,然后调用select对readset集合进行监听(注意select函数中readset为值-结果参数,函数返回时集合内任何未就绪描述符对应的位返回时均清0,因此每次调用select前,都要把readset重新赋值为allset);接下来的for循环检查哪个监听描述符已就绪,对已就绪的监听描述符调用Accept接受用户连接请求,并使用返回的已连接描述符为客户服务。


查看监听状态:

netstat -antp | grep "ruptimed"



0 0