一、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)