Unix网络编程代码 第6章 I/O复用:select、poll和epoll函数

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

你可能感兴趣的:(Unix网络编程代码 第6章 I/O复用:select、poll和epoll函数)