epoll回显服务器

epoll算是LINUX上最高效的服务器开发机制了,相比于select,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率。因为在内核中的select实现中,它是采用轮询来处理的,轮询的fd数目越多,自然耗时越多。

epoll的使用非常简单,只需要使用三个函数就行了:epoll_create,epoll_wait,epoll_ctl,以下是使用epoll来实现回显服务器的一个例子,注释详尽,欢迎大家阅读。

#include <stdio.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <arpa/inet.h>

#define MAXLINE 1024
#define SERV_PORT 8877

//发生了致命错误,输出错误后立即退出
void error_quit(const char *str)    
{    
	perror(str);
	exit(1);    
}   

int main(void)      
{      
	int listenfd, connfd, sockfd, epfd;    
	int i, res, maxi, nfds;    
	ssize_t n;      
	char buf[MAXLINE];      
	socklen_t clilen;      
	struct sockaddr_in cliaddr;      
	struct sockaddr_in servaddr;     

	//声明epoll_event结构体的变量,ev用于注册事件,数组用于回传要处理的事件      
	struct epoll_event ev, events[256];      

	//创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大      
	epfd = epoll_create(256);     
	if( -1 == epfd )
		error_quit("epoll_create error");

	//创建用于TCP协议的套接字        
	listenfd = socket(AF_INET, SOCK_STREAM, 0);      
	memset(&servaddr, 0, sizeof(servaddr));      
	servaddr.sin_family = AF_INET;      
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);      
	servaddr.sin_port = htons(SERV_PORT);      

	//把socket和socket地址结构联系起来       
	res = bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));    
	if( -1 == res )
		error_quit("bind error");

	//开始监听LISTENQ端口       
	res = listen(listenfd, INADDR_ANY);    
	if( -1 == res )
		error_quit("listen error");

	//设置与要处理的事件相关的文件描述符和事件    
	ev.data.fd = listenfd;      
	/*
	events可以是以下几个宏的集合:
	EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
	EPOLLOUT:表示对应的文件描述符可以写;
	EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
	EPOLLERR:表示对应的文件描述符发生错误;
	EPOLLHUP:表示对应的文件描述符被挂断;
	EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
	EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里
	*/
	ev.events = EPOLLIN|EPOLLET;      

	//注册epoll事件      
	epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd,&ev);      
	maxi = 0;    

	while(1)    
	{      
		//等待epoll事件的发生      
		//返回需要处理的事件数目nfds,如返回0表示已超时。      
		nfds = epoll_wait(epfd, events, 20, 500);      

		//处理所发生的所有事件      
		for(i=0; i < nfds; ++i)      
		{      
			//如果新监测到一个SOCKET用户连接到了绑定的SOCKET端口,建立新的连接。      
			if(events[i].data.fd == listenfd)      
			{      
				connfd = accept(listenfd,(struct sockaddr *)&cliaddr, &clilen);  
				if( -1 == connfd )
					error_quit("accept error");    

				//注册用于读操作的文件描述符和事件    
				ev.data.fd = connfd;      
				ev.events = EPOLLIN|EPOLLET;        
				res = epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev);     
				if( -1 == res )
					error_quit("epoll_ctl error");
			}      

			//如果是已经连接的用户,并且收到数据,那么进行读入。      
			else if(events[i].events & EPOLLIN)      
			{      
				sockfd = events[i].data.fd;    
				if ( sockfd < 0 )      
					continue;      
				n = read(sockfd, buf, MAXLINE);    
				if ( n < 0)       
				{        
					// Connection Reset:你连接的那一端已经断开了,    
					//而你却还试着在对方已断开的socketfd上读写数据!      
					if (errno == ECONNRESET)      
					{      
						close(sockfd);      
						events[i].data.fd = -1;      
					}       
					else      
						error_quit("read error");    
				}       
				//如果读入的数据为空      
				else if ( n == 0 )     
				{      
					close(sockfd);      
					events[i].data.fd = -1;      
				}      
				else    
				{    
					//注册用于写操作的文件描述符和事件    
					ev.data.fd = sockfd;    
					ev.events = EPOLLOUT|EPOLLET;     
					res = epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev);    
					if( -1 == res )
						error_quit("epoll_ctl error");
				}    
			}    

			//如果有数据发送    
			else if(events[i].events & EPOLLOUT)     
			{    
				sockfd = events[i].data.fd;    
				write(sockfd, buf, n); 

				//注册用于读操作的文件描述符和事件    
				ev.data.fd = sockfd;    
				ev.events = EPOLLIN|EPOLLET;    
				res = epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev);    
				if( -1 == res )
					error_quit("epoll_ctl error");
			}    
		}    
	}    
	return 0;      
}     


使用telnet作为客户端测试的结果如下:
qch@qch ~/program/tcode $ gcc server.c -o server
qch@qch ~/program/tcode $ ./server &
[1] 4773
qch@qch ~/program/tcode $ telnet localhost 8877
.........
abcde   
abcde
.........

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值