一、web.h

#include    <stdio.h>
#include    <netdb.h>
#include    <errno.h>
#include    <fcntl.h>
#include    <stdlib.h>
#include    <unistd.h>
#include    <strings.h>
#include    <sys/select.h>
#include    <sys/socket.h>

#define     MAXFILES    20
#define     SERV        "80"      /* port number or service name */
#define     MAXLINE     4096

struct file {
  char    *f_name;                /* filename */
  char    *f_host;                /* hostname or IPv4/IPv6 address */
  int     f_fd;                   /* descriptor */
  int     f_flags;                /* F_xxx below */
} file[MAXFILES];

#define        F_CONNECTING    1    /* connect() in progress */
#define        F_READING       2    /* connect() complete; now reading */
#define        F_DONE          4    /* all done */

#define        GET_CMD        "GET %s HTTP/1.0\r\n\r\n"

/* globals */
int     nconn, nfiles;
int     nlefttoconn, nlefttoread;
int     maxfd;
fd_set  rset, wset;

/* function prototypes */
int     min(const int, const int);
int     tcp_connect(const char *, const char *);
void    err_ret(const char *, ...);
void    err_sys(const char *, ...);
void    err_quit(const char *, ...);
void    home_page(const char *, const char *);
void    start_connect(struct file *);
void    write_get_cmd(struct file *);
ssize_t writen(int, const void *, size_t);
struct  addrinfo * host_serv(const char *, const char *, int, int);

 

二、web.c

#include    "web.h"

int main(int argc, char **argv)
{
    int          i, fd, maxnconn;
    int       flags, error;
    char      buf[MAXLINE];
    fd_set      rs, ws;
    socklen_t n;

    if (argc < 5) {
        err_quit("usage: web <conns> <hostname> <homepage> <file1> ...");
    }
    maxnconn = atoi(argv[1]);

    nfiles = min(argc - 4, MAXFILES);
    for (i = 0; i < nfiles; i++) {
        file[i].f_name = argv[i + 4];
        file[i].f_host = argv[2];
        file[i].f_flags = 0;
    }
    printf("nfiles = %d\n", nfiles);

    home_page(argv[2], argv[3]);

    FD_ZERO(&rset);
    FD_ZERO(&wset);
    maxfd = -1;
    nlefttoread = nfiles;
    nlefttoconn = nfiles;
    nconn = 0;

    while (nlefttoread > 0) {
        while (nconn < maxnconn && nlefttoconn > 0) {
            /* find a file to read */
            for (i = 0 ; i < nfiles; i++) {
                if (file[i].f_flags == 0) {
                    break;
                }
            }
            if (i == nfiles) {
                err_quit("nlefttoconn = %d but nothing found", nlefttoconn);
            }
            start_connect(&file[i]);
            nconn++;
            nlefttoconn--;
        }

        rs = rset;
        ws = wset;
        n = select(maxfd+1, &rs, &ws, NULL, NULL);

        for (i = 0; i < nfiles; i++) {
            flags = file[i].f_flags;
            if (flags == 0 || flags & F_DONE) {
                continue;
            }
            fd = file[i].f_fd;
            if (flags & F_CONNECTING &&
                (FD_ISSET(fd, &rs) || FD_ISSET(fd, &ws))) {
                n = sizeof(error);
                if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &n) < 0 ||
                    error != 0) {
                    err_ret("nonblocking connect failed for %s",
                            file[i].f_name);
                }
                /* connection established */
                printf("connection established for %s\n", file[i].f_name);
                FD_CLR(fd, &wset);            /* no more writeability test */
                write_get_cmd(&file[i]);      /* write() the GET command */

            } else if (flags & F_READING && FD_ISSET(fd, &rs)) {
                if ( (n = read(fd, buf, sizeof(buf))) == 0) {
                    printf("end-of-file on %s\n", file[i].f_name);
                    close(fd);
                    file[i].f_flags = F_DONE; /* clears F_READING */
                    FD_CLR(fd, &rset);
                    nconn--;
                    nlefttoread--;
                } else {
                    printf("read %d bytes from %s\n", n, file[i].f_name);
                }
            }
        }
    }
    exit(0);
}

 

三、home_page.c

#include    "web.h"

void home_page(const char *host, const char *fname)
{
    int        fd, n;
    char    line[MAXLINE];

    fd = tcp_connect(host, SERV); /* blocking connect() */

    n = snprintf(line, sizeof(line), GET_CMD, fname);
    if (writen(fd, line, n) == -1) {
        err_sys("write error");        
    }

    for ( ; ; ) {
        if ( (n = read(fd, line, MAXLINE)) == 0) {
            break;                /* server closed connection */
        }
        printf("read %d bytes of home page\n", n);
        /* do whatever with data */
    }
    printf("end-of-file on home page\n");
    close(fd);
}

 

四、start_connect.c

#include    "web.h"

void start_connect(struct file *fptr)
{
    int       fd, flags, n;
    struct addrinfo    *ai;

    ai = host_serv(fptr->f_host, SERV, 0, SOCK_STREAM);

    fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
    fptr->f_fd = fd;
    printf("start_connect for %s, fd %d\n", fptr->f_name, fd);

    /* Set socket nonblocking */
    flags = fcntl(fd, F_GETFL, 0);
    fcntl(fd, F_SETFL, flags | O_NONBLOCK);

    /* Initiate nonblocking connect to the server. */
    if ( (n = connect(fd, ai->ai_addr, ai->ai_addrlen)) < 0) {
        if (errno != EINPROGRESS) {
            err_sys("nonblocking connect error");
        }
        fptr->f_flags = F_CONNECTING;
        FD_SET(fd, &rset);      /* select for reading and writing */
        FD_SET(fd, &wset);
        if (fd > maxfd) {
            maxfd = fd;
        }
    } else if (n >= 0) {        /* connect is already done */
        write_get_cmd(fptr);    /* write() the GET command */
    }
}

 

五、tcp_connect.c

#include    "web.h"

int tcp_connect(const char *host, const char *serv)
{
    int       sockfd, n;
    struct addrinfo    hints;
    struct addrinfo *res, *ressave;

    bzero(&hints, sizeof(struct addrinfo));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;

    if ( (n = getaddrinfo(host, serv, &hints, &res)) != 0) {
        err_quit("tcp_connect error for %s, %s: %s",
                 host, serv, gai_strerror(n));
    }
    ressave = res;

    do {
        sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
        if (sockfd < 0) {
            continue;     /* ignore this one */
        }
        if (connect(sockfd, res->ai_addr, res->ai_addrlen) == 0) {
            break;        /* success */
        }
        close(sockfd);    /* ignore this one */
    } while ( (res = res->ai_next) != NULL);

    if (res == NULL) {    /* errno set from final connect() */
        err_sys("tcp_connect error for %s, %s", host, serv);
    }

    freeaddrinfo(ressave);

    return (sockfd);
}

 

六、host_serv.c

#include    <stddef.h>
#include    <netdb.h>
#include    <strings.h>

struct addrinfo *host_serv(const char *host, 
    const char *serv, int family, int socktype)
{
    int                n;
    struct addrinfo    hints, *res;

    bzero(&hints, sizeof(struct addrinfo));
    hints.ai_flags  = AI_CANONNAME;  /* always return canonical name */
    hints.ai_family = family;        /* AF_UNSPEC, AF_INET, AF_INET6, etc. */
    hints.ai_socktype = socktype;    /* 0, SOCK_STREAM, SOCK_DGRAM, etc. */

    if ( (n = getaddrinfo(host, serv, &hints, &res)) != 0) {
        return(NULL);
    }

    return (res); /* return pointer to first on linked list */
}

 

七、write_get_cmd.c

#include    "web.h"

void write_get_cmd(struct file *fptr)
{
    int        n;
    char    line[MAXLINE];

    n = snprintf(line, sizeof(line), GET_CMD, fptr->f_name);
    writen(fptr->f_fd, line, n);
    printf("wrote %d bytes for %s\n", n, fptr->f_name);

    fptr->f_flags = F_READING;            /* clears F_CONNECTING */

    FD_SET(fptr->f_fd, &rset);            /* will read server's reply */
    if (fptr->f_fd > maxfd) {
        maxfd = fptr->f_fd;
    }
}

 

八、writen.c

#include <unistd.h>
#include <errno.h>

ssize_t writen(int fd, const void *vptr, size_t n) {
    size_t nleft;
    ssize_t nwriten;
    const char *ptr;

    ptr = vptr;
    nleft = n;
    while (nleft > 0) {
        if ( (nwriten = write(fd, ptr, nleft)) <= 0) {
            if (nwriten < 0 && errno == EINTR) {
                nwriten = 0; /* call write() again */
            } else {
                return (-1);
            }
        } 
        nleft -= nwriten;
        ptr += nwriten;
    }
    return (n - nwriten);
}

 

九、others.c

int min(int num1, int num2) {
    return (num1<num2?num1:num2);
}

 

十、error.c

#include    <stdio.h>
#include    <errno.h>
#include    <stdlib.h>
#include    <string.h>
#include    <stdarg.h>        /* ANSI C header file */
#include    <syslog.h>        /* for syslog() */

#define     MAXLINE     4096

int        daemon_proc;       /* set nonzero by daemon_init() */

static void    err_doit(int, int, const char *, va_list);

/* Nonfatal error related to system call
 * Print message and return */

void err_ret(const char *fmt, ...) {

    va_list        ap;    

    va_start(ap, fmt);    
    err_doit(1, LOG_INFO, fmt, ap);
    va_end(ap);
    return;
}

/* Fatal error related to system call
 * Print message and terminate */

void err_sys(const char *fmt, ...) {

    va_list        ap;

    va_start(ap, fmt);
    err_doit(1, LOG_ERR, fmt, ap);
    va_end(ap);
    exit(1);
}

/* Fatal error related to system call
 * Print message, dump core, and terminate */

void err_dump(const char *fmt, ...) {
    va_list        ap;

    va_start(ap, fmt);
    err_doit(1, LOG_ERR, fmt, ap);
    va_end(ap);
    abort();        /* dump core and terminate */
    exit(1);        /* shouldn't get here */
}

/* Nonfatal error unrelated to system call
 * Print message and return */

void err_msg(const char *fmt, ...) {

    va_list        ap;

    va_start(ap, fmt);
    err_doit(0, LOG_INFO, fmt, ap);
    va_end(ap);
    return;
}

/* Fatal error unrelated to system call
 * Print message and terminate */

void err_quit(const char *fmt, ...) {

    va_list        ap;

    va_start(ap, fmt);
    err_doit(0, LOG_ERR, fmt, ap);
    va_end(ap);
    exit(1);
}

/* Print message and return to caller
 * Caller specifies "errnoflag" and "level" */

static void err_doit(int errnoflag, int level, const char *fmt, va_list ap) {

    int        errno_save, n;
    char    buf[MAXLINE + 1];

    errno_save = errno;                /* value caller might want printed */
#ifdef    HAVE_VSNPRINTF
    vsnprintf(buf, MAXLINE, fmt, ap);  /* safe */
#else
    vsprintf(buf, fmt, ap);            /* not safe */
#endif
    n = strlen(buf);
    if (errnoflag)
        snprintf(buf + n, MAXLINE - n, ": %s", strerror(errno_save));
    strcat(buf, "\n");

    if (daemon_proc) {
        syslog(level, buf, NULL);
    } else {
        fflush(stdout);        /* in case stdout and stderr are the same */
        fputs(buf, stderr);
        fflush(stderr);
    }
    return;
}

 

十一、Makefile

cccc = gcc
prom = web
deps = web.h
objs = web.o home_page.o tcp_connect.o start_connect.o write_get_cmd.o writen.o others.o error.o host_serv.o

$(prom): $(objs)
    $(cccc) -o $(prom) $(objs)

%.o: %.c $(deps) 
    $(cccc) -c $< -o $@    

clean: 
    rm -rf $(objs) $(prom)