第6章 I/O复用:select、poll和epoll函数
6.4 str_cli函数(修订版)
//使用select
#define _POSIX_SOURCE
#include <netinet/in.h> /* sockaddr_in{} and other Internet defns */
#include <strings.h>
#include <sys/select.h>
#include <stdarg.h> /* ANSI C header file */
#include <syslog.h> /* for syslog() */
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>
#define MAXLINE 4096 /* max text line length */
#define SERV_PORT 9877 /* TCP and UDP */
int daemon_proc; /* set nonzero by daemon_init() */
#define SA struct sockaddr
#define max(a,b) ((a) > (b) ? (a) : (b))
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, "%s", buf);
} else {
fflush(stdout); /* in case stdout and stderr are the same */
fputs(buf, stderr);
fflush(stderr);
}
return;
}
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);
}
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);
}
int Socket(int family, int type, int protocol)
{
int n;
if ((n = socket(family, type, protocol)) < 0)
err_sys("socket error");
return (n);
}
void Inet_pton(int family, const char *strptr, void *addrptr)
{
int n;
if ((n = inet_pton(family, strptr, addrptr)) < 0)
err_sys("inet_pton error for %s", strptr); /* errno set */
else if (n == 0)
err_quit("inet_pton error for %s", strptr); /* errno not set */
/* nothing to return */
}
void Connect(int fd, const struct sockaddr *sa, socklen_t salen)
{
if (connect(fd, sa, salen) < 0)
err_sys("connect error");
}
ssize_t writen(int fd, const void *vptr, size_t n)
{
size_t nleft;
ssize_t nwritten;
const char *ptr;
ptr = (const char *)vptr;
nleft = n;
while (nleft > 0) {
if ((nwritten = write(fd, ptr, nleft)) <= 0) {
if (nwritten < 0 && errno == EINTR)
nwritten = 0; /* and call write() again */
else
return (-1); /* error */
}
nleft -= nwritten;
ptr += nwritten;
}
return (n);
}
void Writen(int fd, void *ptr, int nbytes)
{
if (writen(fd, ptr, nbytes) != nbytes)
err_sys("writen error");
}
char *Fgets(char *ptr, int n, FILE * stream)
{
char *rptr;
if ((rptr = fgets(ptr, n, stream)) == NULL && ferror(stream))
err_sys("fgets error");
return (rptr);
}
void Fputs(const char *ptr, FILE * stream)
{
if (fputs(ptr, stream) == EOF)
err_sys("fputs error");
}
static int read_cnt;
static char *read_ptr;
static char read_buf[MAXLINE];
static ssize_t my_read(int fd, char *ptr)
{
if (read_cnt <= 0) {
again:
if ((read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) {
if (errno == EINTR)
goto again;
return (-1);
} else if (read_cnt == 0)
return (0);
read_ptr = read_buf;
}
read_cnt--;
*ptr = *read_ptr++;
return (1);
}
ssize_t readline(int fd, void *vptr, int maxlen)
{
ssize_t n, rc;
char c, *ptr;
ptr = (char *)vptr;
for (n = 1; n < maxlen; n++) {
if ((rc = my_read(fd, &c)) == 1) {
*ptr++ = c;
if (c == '\n')
break; /* newline is stored, like fgets() */
} else if (rc == 0) {
*ptr = 0;
return (n - 1); /* EOF, n - 1 bytes were read */
} else
return (-1); /* error, errno set by read() */
}
*ptr = 0; /* null terminate like fgets() */
return (n);
}
ssize_t Readline(int fd, void *ptr, size_t maxlen)
{
ssize_t n;
if ((n = readline(fd, ptr, maxlen)) < 0)
err_sys("readline error");
return (n);
}
int Select(int nfds, fd_set * readfds, fd_set * writefds, fd_set * exceptfds,
struct timeval *timeout)
{
int n;
do {
n = select(nfds, readfds, writefds, exceptfds, timeout);
if (n < 0 && errno != EINTR)
err_sys("select error");
} while (n < 0);
return (n); /* can return 0 on timeout */
}
void str_cli(FILE * fp, int sockfd)
{
int maxfdp1;
fd_set rset;
char sendline[MAXLINE], recvline[MAXLINE];
FD_ZERO(&rset);
for (;;) {
FD_SET(fileno(fp), &rset);
FD_SET(sockfd, &rset);
maxfdp1 = max(fileno(fp), sockfd) + 1;
Select(maxfdp1, &rset, NULL, NULL, NULL);
if (FD_ISSET(sockfd, &rset)) { /* socket is readable */
if (Readline(sockfd, recvline, MAXLINE) == 0)
err_quit
("str_cli: server terminated prematurely");
Fputs(recvline, stdout);
}
if (FD_ISSET(fileno(fp), &rset)) { /* input is readable */
if (Fgets(sendline, MAXLINE, fp) == NULL)
return; /* all done */
Writen(sockfd, sendline, strlen(sendline));
}
}
}
int main(int argc, char **argv)
{
int sockfd;
struct sockaddr_in servaddr;
if (argc != 2)
err_quit("usage: tcpcli <IPaddress>");
sockfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERV_PORT);
Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
Connect(sockfd, (SA *) & servaddr, sizeof(servaddr));
str_cli(stdin, sockfd); /* do it all */
exit(0);
}
6.7 str_cli函数(再修订版)
//使用shutdown
#include <strings.h>
#include <sys/select.h>
#include <netinet/in.h> /* sockaddr_in{} and other Internet defns */
#include <stdarg.h> /* ANSI C header file */
#include <syslog.h> /* for syslog() */
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>
#define MAXLINE 4096 /* max text line length */
#define SERV_PORT 9877 /* TCP and UDP */
int daemon_proc; /* set nonzero by daemon_init() */
#define SA struct sockaddr
#define max(a,b) ((a) > (b) ? (a) : (b))
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, "%s", buf);
} else {
fflush(stdout); /* in case stdout and stderr are the same */
fputs(buf, stderr);
fflush(stderr);
}
return;
}
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);
}
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);
}
int Socket(int family, int type, int protocol)
{
int n;
if ((n = socket(family, type, protocol)) < 0)
err_sys("socket error");
return (n);
}
void Inet_pton(int family, const char *strptr, void *addrptr)
{
int n;
if ((n = inet_pton(family, strptr, addrptr)) < 0)
err_sys("inet_pton error for %s", strptr); /* errno set */
else if (n == 0)
err_quit("inet_pton error for %s", strptr); /* errno not set */
/* nothing to return */
}
void Connect(int fd, const struct sockaddr *sa, socklen_t salen)
{
if (connect(fd, sa, salen) < 0)
err_sys("connect error");
}
ssize_t writen(int fd, const void *vptr, size_t n)
{
size_t nleft;
ssize_t nwritten;
const char *ptr;
ptr = (const char *)vptr;
nleft = n;
while (nleft > 0) {
if ((nwritten = write(fd, ptr, nleft)) <= 0) {
if (nwritten < 0 && errno == EINTR)
nwritten = 0; /* and call write() again */
else
return (-1); /* error */
}
nleft -= nwritten;
ptr += nwritten;
}
return (n);
}
void Writen(int fd, void *ptr, int nbytes)
{
if (writen(fd, ptr, nbytes) != nbytes)
err_sys("writen error");
}
char *Fgets(char *ptr, int n, FILE * stream)
{
char *rptr;
if ((rptr = fgets(ptr, n, stream)) == NULL && ferror(stream))
err_sys("fgets error");
return (rptr);
}
void Fputs(const char *ptr, FILE * stream)
{
if (fputs(ptr, stream) == EOF)
err_sys("fputs error");
}
int Select(int nfds, fd_set * readfds, fd_set * writefds, fd_set * exceptfds,
struct timeval *timeout)
{
int n;
do {
n = select(nfds, readfds, writefds, exceptfds, timeout);
if (n < 0 && errno != EINTR)
err_sys("select error");
} while (n < 0);
return (n); /* can return 0 on timeout */
}
ssize_t Read(int fd, void *ptr, size_t nbytes)
{
ssize_t n;
if ((n = read(fd, ptr, nbytes)) == -1)
err_sys("read error");
return (n);
}
void Write(int fd, void *ptr, int nbytes)
{
if (write(fd, ptr, nbytes) != nbytes)
err_sys("write error");
}
void Shutdown(int fd, int how)
{
if (shutdown(fd, how) < 0)
err_sys("shutdown error");
}
void str_cli(int fd, int sockfd)
{
int maxfdp1, stdineof;
fd_set rset;
char buf[MAXLINE];
int n;
stdineof = 0;
FD_ZERO(&rset);
for (;;) {
if (stdineof == 0)
FD_SET(fd, &rset);
FD_SET(sockfd, &rset);
maxfdp1 = max(fd, sockfd) + 1;
Select(maxfdp1, &rset, NULL, NULL, NULL);
if (FD_ISSET(sockfd, &rset)) { /* socket is readable */
if ((n = Read(sockfd, buf, MAXLINE)) == 0) {
if (stdineof == 1)
return; /* normal termination */
else
err_quit
("str_cli: server terminated prematurely");
}
Write(STDOUT_FILENO, buf, n);
}
if (FD_ISSET(fd, &rset)) { /* input is readable */
if ((n = Read(fd, buf, MAXLINE)) == 0) {
stdineof = 1;
Shutdown(sockfd, SHUT_WR); /* send FIN */
FD_CLR(fd, &rset);
continue;
}
Writen(sockfd, buf, n);
}
}
}
int main(int argc, char **argv)
{
int sockfd;
struct sockaddr_in servaddr;
if (argc != 2)
err_quit("usage: tcpcli <IPaddress>");
sockfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERV_PORT);
Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
Connect(sockfd, (SA *) & servaddr, sizeof(servaddr));
str_cli(STDIN_FILENO, sockfd); /* do it all */
exit(0);
}
6.8 TCP回射服务器程序(修订版)
//使用select
#include <sys/types.h> /* basic system data types */
#include <strings.h>
#include <sys/select.h>
#include <sys/socket.h> /* basic socket definitions */
#include <netinet/in.h> /* sockaddr_in{} and other Internet defns */
#include <stdarg.h> /* ANSI C header file */
#include <syslog.h> /* for syslog() */
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
int daemon_proc; /* set nonzero by daemon_init() */
#define SA struct sockaddr
#define MAXLINE 4096 /* max text line length */
#define SERV_PORT 9877 /* TCP and UDP */
#define LISTENQ 1024 /* 2nd argument to listen() */
typedef void Sigfunc(int); /* for signal handlers */
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, "%s", buf);
} else {
fflush(stdout); /* in case stdout and stderr are the same */
fputs(buf, stderr);
fflush(stderr);
}
return;
}
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);
}
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);
}
int Socket(int family, int type, int protocol)
{
int n;
if ((n = socket(family, type, protocol)) < 0)
err_sys("socket error");
return (n);
}
void Bind(int fd, const struct sockaddr *sa, socklen_t salen)
{
if (bind(fd, sa, salen) < 0)
err_sys("bind error");
}
void Listen(int fd, int backlog)
{
char *ptr;
/*4can override 2nd argument with environment variable */
if ((ptr = getenv("LISTENQ")) != NULL)
backlog = atoi(ptr);
if (listen(fd, backlog) < 0)
err_sys("listen error");
}
int Accept(int fd, struct sockaddr *sa, socklen_t * salenptr)
{
int n;
again:
if ((n = accept(fd, sa, salenptr)) < 0) {
#ifdef EPROTO
if (errno == EPROTO || errno == ECONNABORTED)
#else
if (errno == ECONNABORTED)
#endif
goto again;
else
err_sys("accept error");
}
return (n);
}
void Close(int fd)
{
if (close(fd) == -1)
err_sys("close error");
}
ssize_t writen(int fd, const void *vptr, size_t n)
{
size_t nleft;
ssize_t nwritten;
const char *ptr;
ptr = (const char *)vptr;
nleft = n;
while (nleft > 0) {
if ((nwritten = write(fd, ptr, nleft)) <= 0) {
if (nwritten < 0 && errno == EINTR)
nwritten = 0; /* and call write() again */
else
return (-1); /* error */
}
nleft -= nwritten;
ptr += nwritten;
}
return (n);
}
void Writen(int fd, void *ptr, int nbytes)
{
if (writen(fd, ptr, nbytes) != nbytes)
err_sys("writen error");
}
void str_echo(int sockfd)
{
ssize_t n;
char buf[MAXLINE];
again:
while ((n = read(sockfd, buf, MAXLINE)) > 0)
Writen(sockfd, buf, n);
if (n < 0 && errno == EINTR)
goto again;
else if (n < 0)
err_sys("str_echo: read error");
}
int Select(int nfds, fd_set * readfds, fd_set * writefds, fd_set * exceptfds,
struct timeval *timeout)
{
int n;
do {
n = select(nfds, readfds, writefds, exceptfds, timeout);
if (n < 0 && errno != EINTR)
err_sys("select error");
} while (n < 0);
return (n); /* can return 0 on timeout */
}
ssize_t Read(int fd, void *ptr, size_t nbytes)
{
ssize_t n;
if ((n = read(fd, ptr, nbytes)) == -1)
err_sys("read error");
return (n);
}
int main()
{
int i, maxi, maxfd, listenfd, connfd, sockfd;
int nready, client[FD_SETSIZE];
ssize_t n;
fd_set rset, allset;
char buf[MAXLINE];
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
Bind(listenfd, (SA *) & servaddr, sizeof(servaddr));
Listen(listenfd, LISTENQ);
maxfd = listenfd; /* initialize */
maxi = -1; /* index into client[] array */
for (i = 0; i < FD_SETSIZE; i++)
client[i] = -1; /* -1 indicates available entry */
FD_ZERO(&allset);
FD_SET(listenfd, &allset);
/* end fig01 */
/* include fig02 */
for (;;) {
rset = allset; /* structure assignment */
nready = Select(maxfd + 1, &rset, NULL, NULL, NULL);
if (FD_ISSET(listenfd, &rset)) { /* new client connection */
clilen = sizeof(cliaddr);
connfd = Accept(listenfd, (SA *) & cliaddr, &clilen);
#ifdef NOTDEF
printf("new client: %s, port %d\n",
Inet_ntop(AF_INET, &cliaddr.sin_addr, 4, NULL),
ntohs(cliaddr.sin_port));
#endif
for (i = 0; i < FD_SETSIZE; i++)
if (client[i] < 0) {
client[i] = connfd; /* save descriptor */
break;
}
if (i == FD_SETSIZE)
err_quit("too many clients");
FD_SET(connfd, &allset); /* add new descriptor to set */
if (connfd > maxfd)
maxfd = connfd; /* for select */
if (i > maxi)
maxi = i; /* max index in client[] array */
if (--nready <= 0)
continue; /* no more readable descriptors */
}
for (i = 0; i <= maxi; i++) { /* check all clients for data */
if ((sockfd = client[i]) < 0)
continue;
if (FD_ISSET(sockfd, &rset)) {
if ((n = Read(sockfd, buf, MAXLINE)) == 0) {
/*4connection closed by client */
Close(sockfd);
FD_CLR(sockfd, &allset);
client[i] = -1;
} else
Writen(sockfd, buf, n);
if (--nready <= 0)
break; /* no more readable descriptors */
}
}
}
}
6.11 TCP回射服务器程序(再修订版)
//使用poll
#include <strings.h>
#include <sys/types.h> /* basic system data types */
#include <sys/socket.h> /* basic socket definitions */
#include <netinet/in.h> /* sockaddr_in{} and other Internet defns */
#include <stdarg.h> /* ANSI C header file */
#include <syslog.h> /* for syslog() */
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <poll.h> /* for convenience */
int daemon_proc; /* set nonzero by daemon_init() */
#define SA struct sockaddr
#define MAXLINE 4096 /* max text line length */
#define SERV_PORT 9877 /* TCP and UDP */
#define LISTENQ 1024 /* 2nd argument to listen() */
#define OPEN_MAX 1024
#define INFTIM (-1) /* infinite poll timeout */
typedef void Sigfunc(int); /* for signal handlers */
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, "%s", buf);
} else {
fflush(stdout); /* in case stdout and stderr are the same */
fputs(buf, stderr);
fflush(stderr);
}
return;
}
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);
}
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);
}
int Socket(int family, int type, int protocol)
{
int n;
if ((n = socket(family, type, protocol)) < 0)
err_sys("socket error");
return (n);
}
void Bind(int fd, const struct sockaddr *sa, socklen_t salen)
{
if (bind(fd, sa, salen) < 0)
err_sys("bind error");
}
void Listen(int fd, int backlog)
{
char *ptr;
/*4can override 2nd argument with environment variable */
if ((ptr = getenv("LISTENQ")) != NULL)
backlog = atoi(ptr);
if (listen(fd, backlog) < 0)
err_sys("listen error");
}
int Accept(int fd, struct sockaddr *sa, socklen_t * salenptr)
{
int n;
again:
if ((n = accept(fd, sa, salenptr)) < 0) {
#ifdef EPROTO
if (errno == EPROTO || errno == ECONNABORTED)
#else
if (errno == ECONNABORTED)
#endif
goto again;
else
err_sys("accept error");
}
return (n);
}
void Close(int fd)
{
if (close(fd) == -1)
err_sys("close error");
}
ssize_t writen(int fd, const void *vptr, size_t n)
{
size_t nleft;
ssize_t nwritten;
const char *ptr;
ptr = (const char *)vptr;
nleft = n;
while (nleft > 0) {
if ((nwritten = write(fd, ptr, nleft)) <= 0) {
if (nwritten < 0 && errno == EINTR)
nwritten = 0; /* and call write() again */
else
return (-1); /* error */
}
nleft -= nwritten;
ptr += nwritten;
}
return (n);
}
void Writen(int fd, void *ptr, int nbytes)
{
if (writen(fd, ptr, nbytes) != nbytes)
err_sys("writen error");
}
void str_echo(int sockfd)
{
ssize_t n;
char buf[MAXLINE];
again:
while ((n = read(sockfd, buf, MAXLINE)) > 0)
Writen(sockfd, buf, n);
if (n < 0 && errno == EINTR)
goto again;
else if (n < 0)
err_sys("str_echo: read error");
}
ssize_t Read(int fd, void *ptr, size_t nbytes)
{
ssize_t n;
if ((n = read(fd, ptr, nbytes)) == -1)
err_sys("read error");
return (n);
}
int Poll(struct pollfd *fdarray, unsigned long nfds, int timeout)
{
int n;
do {
n = poll(fdarray, nfds, timeout);
if (n < 0 && errno != EINTR)
err_sys("poll error");
} while (n < 0);
return (n);
}
int main()
{
int i, maxi, listenfd, connfd, sockfd;
int nready;
ssize_t n;
char buf[MAXLINE];
socklen_t clilen;
struct pollfd client[OPEN_MAX];
struct sockaddr_in cliaddr, servaddr;
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
Bind(listenfd, (SA *) & servaddr, sizeof(servaddr));
Listen(listenfd, LISTENQ);
client[0].fd = listenfd;
client[0].events = POLLIN;
for (i = 1; i < OPEN_MAX; i++)
client[i].fd = -1; /* -1 indicates available entry */
maxi = 0; /* max index into client[] array */
/* end fig01 */
/* include fig02 */
for (;;) {
nready = Poll(client, maxi + 1, INFTIM);
if (client[0].revents & POLLIN) { /* new client connection */
clilen = sizeof(cliaddr);
connfd = Accept(listenfd, (SA *) & cliaddr, &clilen);
#ifdef NOTDEF
printf("new client: %s\n",
Sock_ntop((SA *) & cliaddr, clilen));
#endif
for (i = 1; i < OPEN_MAX; i++)
if (client[i].fd < 0) {
client[i].fd = connfd; /* save descriptor */
break;
}
if (i == OPEN_MAX)
err_quit("too many clients");
client[i].events = POLLIN;
if (i > maxi)
maxi = i; /* max index in client[] array */
if (--nready <= 0)
continue; /* no more readable descriptors */
}
for (i = 1; i <= maxi; i++) { /* check all clients for data */
if ((sockfd = client[i].fd) < 0)
continue;
if (client[i].revents & (POLLIN | POLLERR)) {
if ((n = read(sockfd, buf, MAXLINE)) < 0) {
if (errno == ECONNRESET) {
/*4connection reset by client */
#ifdef NOTDEF
printf
("client[%d] aborted connection\n",
i);
#endif
Close(sockfd);
client[i].fd = -1;
} else
err_sys("read error");
} else if (n == 0) {
/*4connection closed by client */
#ifdef NOTDEF
printf("client[%d] closed connection\n",
i);
#endif
Close(sockfd);
client[i].fd = -1;
} else
Writen(sockfd, buf, n);
if (--nready <= 0)
break; /* no more readable descriptors */
}
}
}
}
6.12 TCP回射服务器程序(再修订版)
//使用epoll,根据6.11的程序修改而来
#include <strings.h>
#include <sys/select.h>
#include <sys/types.h> /* basic system data types */
#include <sys/socket.h> /* basic socket definitions */
#include <netinet/in.h> /* sockaddr_in{} and other Internet defns */
#include <stdarg.h> /* ANSI C header file */
#include <syslog.h> /* for syslog() */
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/epoll.h>
int daemon_proc; /* set nonzero by daemon_init() */
#define SA struct sockaddr
#define MAXLINE 4096 /* max text line length */
#define SERV_PORT 9877 /* TCP and UDP */
#define LISTENQ 1024 /* 2nd argument to listen() */
#define MAX_EVENTS 500
typedef void Sigfunc(int); /* for signal handlers */
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, "%s", buf);
} else {
fflush(stdout); /* in case stdout and stderr are the same */
fputs(buf, stderr);
fflush(stderr);
}
return;
}
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);
}
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);
}
int Socket(int family, int type, int protocol)
{
int n;
if ((n = socket(family, type, protocol)) < 0)
err_sys("socket error");
return (n);
}
void Bind(int fd, const struct sockaddr *sa, socklen_t salen)
{
if (bind(fd, sa, salen) < 0)
err_sys("bind error");
}
void Listen(int fd, int backlog)
{
char *ptr;
/*4can override 2nd argument with environment variable */
if ((ptr = getenv("LISTENQ")) != NULL)
backlog = atoi(ptr);
if (listen(fd, backlog) < 0)
err_sys("listen error");
}
int Accept(int fd, struct sockaddr *sa, socklen_t * salenptr)
{
int n;
again:
if ((n = accept(fd, sa, salenptr)) < 0) {
#ifdef EPROTO
if (errno == EPROTO || errno == ECONNABORTED)
#else
if (errno == ECONNABORTED)
#endif
goto again;
else
err_sys("accept error");
}
return (n);
}
void Close(int fd)
{
if (close(fd) == -1)
err_sys("close error");
}
ssize_t writen(int fd, const void *vptr, size_t n)
{
size_t nleft;
ssize_t nwritten;
const char *ptr;
ptr = (const char *)vptr;
nleft = n;
while (nleft > 0) {
if ((nwritten = write(fd, ptr, nleft)) <= 0) {
if (nwritten < 0 && errno == EINTR)
nwritten = 0; /* and call write() again */
else
return (-1); /* error */
}
nleft -= nwritten;
ptr += nwritten;
}
return (n);
}
void Writen(int fd, void *ptr, int nbytes)
{
if (writen(fd, ptr, nbytes) != nbytes)
err_sys("writen error");
}
void str_echo(int sockfd)
{
ssize_t n;
char buf[MAXLINE];
again:
while ((n = read(sockfd, buf, MAXLINE)) > 0)
Writen(sockfd, buf, n);
if (n < 0 && errno == EINTR)
goto again;
else if (n < 0)
err_sys("str_echo: read error");
}
ssize_t Read(int fd, void *ptr, size_t nbytes)
{
ssize_t n;
if ((n = read(fd, ptr, nbytes)) == -1)
err_sys("read error");
return (n);
}
void Epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
{
if (epoll_ctl(epfd, op, fd, event) < 0)
err_sys("epoll_ctl error");
}
int Epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)
{
int n;
do {
n = epoll_wait(epfd, events, maxevents, timeout);
if (n < 0 && errno != EINTR)
err_sys("epoll_wait error");
} while (n < 0);
return (n);
}
int main()
{
char buf[MAXLINE];
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
int listenfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
Bind(listenfd, (SA *) & servaddr, sizeof(servaddr));
Listen(listenfd, LISTENQ);
// create epoll
int epfd = epoll_create(MAX_EVENTS);
struct epoll_event ev;
ev.data.fd = listenfd;
ev.events = EPOLLIN;
Epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);
struct epoll_event revents[MAX_EVENTS]; //returned events
for (;;) {
int nready = Epoll_wait(epfd, revents, MAX_EVENTS, -1);
for (int i = 0; i < nready; i++) {
int sockfd = revents[i].data.fd;
if (sockfd == listenfd) { /* new client connection */
clilen = sizeof(cliaddr);
int connfd =
Accept(listenfd, (SA *) & cliaddr, &clilen);
ev.data.fd = connfd;
ev.events = EPOLLIN;
Epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev);
} else {
if (revents[i].events & (EPOLLIN | EPOLLERR)) {
int n = read(sockfd, buf, MAXLINE);
if (n > 0) {
Writen(sockfd, buf, n);
} else if (n == 0) {
/*4connection closed by client */
struct epoll_event ev;
ev.data.fd = sockfd;
Epoll_ctl(epfd,
EPOLL_CTL_DEL, sockfd,
&ev);
Close(sockfd);
} else if (n < 0) {
if (errno == ECONNRESET) {
/*4connection reset by client */
struct epoll_event ev;
ev.data.fd = sockfd;
Epoll_ctl(epfd,
EPOLL_CTL_DEL,
sockfd, &ev);
Close(sockfd);
} else {
err_sys("read error");
}
}
}
}
}
}
}
6.13 TCP回射客户程序
//使用epoll,根据6.7修改而来
#include <strings.h>
#include <sys/epoll.h>
#include <netinet/in.h> /* sockaddr_in{} and other Internet defns */
#include <stdarg.h> /* ANSI C header file */
#include <syslog.h> /* for syslog() */
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>
#define MAXLINE 4096 /* max text line length */
#define SERV_PORT 9877 /* TCP and UDP */
int daemon_proc; /* set nonzero by daemon_init() */
#define SA struct sockaddr
#define max(a,b) ((a) > (b) ? (a) : (b))
#define MAX_EVENTS 500
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, "%s", buf);
} else {
fflush(stdout); /* in case stdout and stderr are the same */
fputs(buf, stderr);
fflush(stderr);
}
return;
}
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);
}
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);
}
int Socket(int family, int type, int protocol)
{
int n;
if ((n = socket(family, type, protocol)) < 0)
err_sys("socket error");
return (n);
}
void Inet_pton(int family, const char *strptr, void *addrptr)
{
int n;
if ((n = inet_pton(family, strptr, addrptr)) < 0)
err_sys("inet_pton error for %s", strptr); /* errno set */
else if (n == 0)
err_quit("inet_pton error for %s", strptr); /* errno not set */
/* nothing to return */
}
void Connect(int fd, const struct sockaddr *sa, socklen_t salen)
{
if (connect(fd, sa, salen) < 0)
err_sys("connect error");
}
ssize_t writen(int fd, const void *vptr, size_t n)
{
size_t nleft;
ssize_t nwritten;
const char *ptr;
ptr = (const char *)vptr;
nleft = n;
while (nleft > 0) {
if ((nwritten = write(fd, ptr, nleft)) <= 0) {
if (nwritten < 0 && errno == EINTR)
nwritten = 0; /* and call write() again */
else
return (-1); /* error */
}
nleft -= nwritten;
ptr += nwritten;
}
return (n);
}
void Writen(int fd, void *ptr, int nbytes)
{
if (writen(fd, ptr, nbytes) != nbytes)
err_sys("writen error");
}
ssize_t Read(int fd, void *ptr, size_t nbytes)
{
ssize_t n;
if ((n = read(fd, ptr, nbytes)) == -1)
err_sys("read error");
return (n);
}
void Write(int fd, void *ptr, int nbytes)
{
if (write(fd, ptr, nbytes) != nbytes)
err_sys("write error");
}
void Shutdown(int fd, int how)
{
if (shutdown(fd, how) < 0)
err_sys("shutdown error");
}
void Epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
{
if (epoll_ctl(epfd, op, fd, event) < 0)
err_sys("epoll_ctl error");
}
int Epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)
{
int n;
do {
n = epoll_wait(epfd, events, maxevents, timeout);
if (n < 0 && errno != EINTR)
err_sys("epoll_wait error");
} while (n < 0);
return (n);
}
void str_cli(int fd, int sockfd)
{
// create epoll
int epfd = epoll_create(MAX_EVENTS);
struct epoll_event ev;
ev.data.fd = fd;
ev.events = EPOLLIN;
Epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);
ev.data.fd = sockfd;
ev.events = EPOLLIN;
Epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev);
struct epoll_event revents[MAX_EVENTS]; //returned events
char buf[MAXLINE];
int stdineof = 0;
int n;
for (;;) {
int nready = Epoll_wait(epfd, revents, MAX_EVENTS, -1);
for (int i = 0; i < nready; i++) {
if (revents[i].data.fd == sockfd) { /* socket is readable */
if ((n = Read(sockfd, buf, MAXLINE)) == 0) {
if (stdineof == 1) {
struct epoll_event ev;
ev.data.fd = sockfd;
Epoll_ctl(epfd,
EPOLL_CTL_DEL, sockfd,
&ev);
return; /* normal termination */
} else
err_quit
("str_cli: server terminated prematurely");
}
Write(STDOUT_FILENO, buf, n);
}
if (revents[i].data.fd == fd) { /* input is readable */
if ((n = Read(fd, buf, MAXLINE)) == 0) {
stdineof = 1;
Shutdown(sockfd, SHUT_WR); /* send FIN */
struct epoll_event ev;
ev.data.fd = fd;
Epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &ev);
continue;
}
Writen(sockfd, buf, n);
}
}
}
}
int main(int argc, char **argv)
{
int sockfd;
struct sockaddr_in servaddr;
if (argc != 2)
err_quit("usage: tcpcli <IPaddress>");
sockfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERV_PORT);
Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
Connect(sockfd, (SA *) & servaddr, sizeof(servaddr));
str_cli(STDIN_FILENO, sockfd); /* do it all */
exit(0);
}