《UNIX网络编程 卷1》 笔记补充内容: 高级轮询技术epoll

epoll是Linux提供的高级轮询技术,《UNIX网络编程 卷1》成书时还没有这种技术。它的效率要比单独使用poll函数高很多,非常适合监听大量的描述符,许多高性能的服务器都使用epoll。下面是epoll提供的API,具体使用请参考下面的代码和man手册。

 

/*创建一个epoll实例,返回其描述符*/
int epoll_create(int size);
int epoll_create1(int flags);

/*epoll实例的控制接口,比如添加描述符等*/
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

/*等待一个epoll实例上I/O事件发生*/
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
int epoll_pwait(int epfd, struct epoll_event *events, int maxevents, int timeout, const sigset_t *sigmask);

本节我们使用epoll来实现TCP回显服务器程序,代码如下:

 

 

#include "unp.h"

#define MAX_EVENTS 1024

int main(int argc, char **argv)
{
	int epollfd, listenfd, connfd, sockfd;
	int i, nready;
	ssize_t n;
	char buf[MAXLINE];
	socklen_t clilen;
	struct sockaddr_in cliaddr, servaddr;
	struct epoll_event ev, events[MAX_EVENTS];
	
	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);
	
	epollfd = epoll_create1(0); /*创建一个epoll实例*/
	if (epollfd == -1) {
		err_sys("epoll_create1");
	}
	/*将服务器监听描述符加入epoll实例监听*/
	ev.events = EPOLLIN;
	ev.data.fd = listenfd;
	if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &ev) == -1) { 
		err_sys("epoll_ctl: listenfd");
	}
	
	for ( ; ; ) {
		nready = epoll_wait(epollfd, events, MAX_EVENTS, -1); /*等待I/O事件发生*/
		if (nready == -1) {
			err_sys("epoll_wait");
		}
		for (i = 0; i < nready; i++) {
			if (events[i].data.fd == listenfd) { /*有新的连接*/
				connfd = Accept(listenfd, (struct sockaddr *) &cliaddr, &clilen);
				/*将客户套接字描述符设置为非阻塞*/
				Fcntl(connfd, F_SETFL, O_NONBLOCK);
				/*将客户套接字描述符加入epoll实例监听*/
				ev.events = EPOLLIN | EPOLLET; /*边沿触发方式工作*/
				ev.data.fd = connfd;
				if (epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &ev) == -1) {
					err_sys("epoll_ctl: connfd");
				}
			} else { /*有新的数据到达*/
				sockfd = events[i].data.fd;
				if ((n = read(sockfd, buf, MAXLINE)) < 0) {
					if (errno == ECONNRESET) { /*客户异常关闭连接*/
						/*从epoll实例监听的描述符中移除客户套接字描述符*/
						if (epoll_ctl(epollfd, EPOLL_CTL_DEL, sockfd, NULL) == -1) {
							err_sys("epoll_ctl: sockfd");
						}
						Close(sockfd);
					} else
						err_sys("read error");
				} else if (n == 0) { /*客户正常关闭连接*/
					if (epoll_ctl(epollfd, EPOLL_CTL_DEL, sockfd, NULL) == -1) {
						err_sys("epoll_ctl: sockfd");
					}
					Close(sockfd);
				} else
					Writen(sockfd, buf, n);
			}
		}
	}
}

 

之前我们使用poll函数实现的版本在这里。抛开底层实现不谈,对本例而言,我们单纯从代码的角度就可以看出epoll技术优于使用poll函数的轮询技术。

 

你可能感兴趣的:(UNIX网络编程,卷1,Linux,网络编程,epoll)