高级I/O复用技术:Epoll的使用及一个完整的C实例

高性能的网络服务器需要同时并发处理大量的客户端,而采用以前的那种对每个连接使用一个分开的线程或进程方法效率不高,因为处理大量客户端的时候,资源的使用及进程上下文的切换将会影响服务器的性能。一个可替代的方法是在一个单一的线程中使用非阻塞的I/O(non-blocking I/O)。

        这篇文章主要介绍linux下的epoll(7)方法,其有着良好的就绪事件通知机制。我们将会使用C来展现一个完整的TCP服务器实现代码。Epoll是被linux2.6开始引进的,但是不被其他的类UNIX系统支持,它提供了一种类似select或poll函数的机制:

   1.Select(2)只能够同时管理FD_SETSIZE数目的文件描述符

       2. poll(2)没有固定的描述符上限这一限制,但是每次必须遍历所有的描述符来检查就绪的描述符,这个过程的时间复杂度为O(N)。

      epoll没有select这样对文件描述符上限的限制,也不会像poll那样进行线性的遍历。因此epoll处理大并发连接有着更高的性能。

    Epoll相关操作函数介绍:

     1. epoll_create(2) or epoll_create1(2)(有着不同的参数值)用来创建epoll实例。

/usr/include/sys/epoll.h
extern int epoll_create (int __size) ;
RETURN:>0, 成功;-1, 出错

函数描述:

       (1) epoll_create返回的是一个文件描述符,也就是说epoll是以特殊文件的方式体现给用户

       (2) __size提示操作系统,用户可能要使用多少个文件描述符,该参数已经废弃,填写一个大于0的正整数

      2.  epoll_ctl(2)用来增加或移除被epoll所监听的文件描述符。

int epoll_ctl(int epfd, int op, int fd, struct 	epoll_event *event);
RETURN:0,成功;-1,出错

函数描述:

        (1) epfd为epoll_create创建的epoll描述符

        (2) epoll_ctl函数对epoll进行op类型的操作,op选项为

              EPOLL_CTL_ADD,对fd描述符注册event事件

              EPOLL_CTL_MOD,对fd描述符的event事件进行修改

              EPOLL_CTL_DEL,删除已注册的event事件

      3. epoll_wait(2)用来等待发生在监听描述符上的事件。它会一直阻塞直到事件发生。

#include <sys/epoll.h>
int epoll_wait(int epfd, struct epoll_event *events,
				int maxevents, int timeout);
RETURN:>0,发生事件个数;=0,时间到;-1,出错

函数描述:

           epoll_wait与select函数类似,同步地等待事件发生

           (1) epfd,标识epoll的文件描述符

           (2) events,指向传入操作系统的一个epoll_event数组

           (3) maxevents,表示传入数组的大小,必须大于0

          当有事件发生,Linux会填写events结构,返回给应用程序。由于epoll_wait同步等待,有可能被信号中断,返回EINTR错误

      更多的函数介绍请参照man。 

Epoll的两种模式:

      1. 水平触发(LT):使用此种模式,当数据可读的时候,epoll_wait()将会一直返回就绪事件。如果你没有处理完全部数据,并且再次在该epoll实例上调用epoll_wait()才监听描述符的时候,它将会再次返回就绪事件,因为有数据可读。ET只支持非阻塞socket。

      2. 边缘触发(ET):使用此种模式,只能获取一次就绪通知,如果没有处理完全部数据,并且再次调用epoll_wait()的时候,它将会阻塞,因为就绪事件已经释放出来了。

ET的效能更高,但是对程序员的要求也更高。在ET模式下,我们必须一次干净而彻底地处理完所有事件。LT两种模式的socket都支持。

     传递给epoll_ctl(2)的Epoll事件结构体如下所示:

typedefunionepoll_data
{
void*ptr;
intfd;
  __uint32_t   u32;
  __uint64_t   u64;
}epoll_data_t;

structepoll_event
{
  __uint32_t   events;/* Epoll events */
epoll_data_t data;/* User data variable */
};

      对于每一个监听的描述符,能够关联一个整形数据或指向用户数据的指针。

      epoll的事件类型:

enum EPOLL_EVENTS
  {
    EPOLLIN = 0x001,
#define EPOLLIN EPOLLIN
    EPOLLPRI = 0x002,
#define EPOLLPRI EPOLLPRI
    EPOLLOUT = 0x004,
#define EPOLLOUT EPOLLOUT
    EPOLLRDNORM = 0x040,
#define EPOLLRDNORM EPOLLRDNORM
    EPOLLRDBAND = 0x080,
#define EPOLLRDBAND EPOLLRDBAND
    EPOLLWRNORM = 0x100,
#define EPOLLWRNORM EPOLLWRNORM
    EPOLLWRBAND = 0x200,
#define EPOLLWRBAND EPOLLWRBAND
    EPOLLMSG = 0x400,
#define EPOLLMSG EPOLLMSG
    EPOLLERR = 0x008,
#define EPOLLERR EPOLLERR
    EPOLLHUP = 0x010,
#define EPOLLHUP EPOLLHUP
    EPOLLRDHUP = 0x2000,
#define EPOLLRDHUP EPOLLRDHUP
    EPOLLONESHOT = (1 << 30),
#define EPOLLONESHOT EPOLLONESHOT
    EPOLLET = (1 << 31)
#define EPOLLET EPOLLET
  };

– EPOLLIN,读事件

– EPOLLOUT,写事件

– EPOLLPRI,带外数据,与select的异常事件集合对应

– EPOLLRDHUP,TCP连接对端至少写写半关闭

– EPOLLERR,错误事件

– EPOLLET,设置事件为边沿触发

– EPOLLONESHOT,只触发一次,事件自动被删除

      epoll在一个文件描述符上只能有一个事件,在一个描述符上添加多个事件,会产生EEXIST的错误。同样,删除epoll的事件,只需描述符就够了

      epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);

      这里有一个比较重要的问题:从epoll_wait返回的events中,该如何知道是哪个描述符上的事件:在注册epoll事件的时候,一定要填写epoll_data,否则我们将分不清触发的是哪个描述符上的事件。

     下面我们将实现一个轻型TCP服务器,功能是在标准输出中打印发送给套接字的一切数据。

/*
 * =====================================================================================
 *
 *       Filename:  EpollServer.c
 *
 *    Description:  this is a epoll server example
 *
 *        Version:  1.0
 *        Created:  2012年03月15日 20时24分26秒
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  LGP (), [email protected]
 *        Company:
 *
 * =====================================================================================
 */

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

/*struct addrinfo
{
	int              ai_flags;
	int              ai_family;
	int              ai_socktype;
	int              ai_protocol;
	size_t           ai_addrlen;
	struct sockaddr *ai_addr;
	char            *ai_canonname;
	struct addrinfo *ai_next;
}; */

static int create_and_bind(char* port)
{
	struct addrinfo hints;
	struct addrinfo*result,*rp;
	int s,sfd;

	memset(&hints,0,sizeof(struct addrinfo));
	hints.ai_family= AF_UNSPEC;/* Return IPv4 and IPv6 */
	hints.ai_socktype= SOCK_STREAM;/* TCP socket */
	hints.ai_flags= AI_PASSIVE;/* All interfaces */

	s = getaddrinfo(NULL, port,&hints,&result); //more info about getaddrinfo() please see:man getaddrinfo!
	if(s != 0)
	{
		fprintf(stderr,"getaddrinfo: %s\n",gai_strerror(s));
		return -1;
	}
	for(rp= result;rp!= NULL;rp=rp->ai_next)
	{
		sfd = socket(rp->ai_family,rp->ai_socktype,rp->ai_protocol);
		if(sfd==-1)
			continue;
		s =bind(sfd,rp->ai_addr,rp->ai_addrlen);
		if(s ==0)
		{
			/* We managed to bind successfully! */
			break;
		}
		close(sfd);
	}

	if(rp== NULL)
	{
		fprintf(stderr,"Could not bind\n");
		return-1;
	}
	freeaddrinfo(result);
	return sfd;
}

static int make_socket_non_blocking(int sfd)
{
	int flags, s;
	flags = fcntl(sfd, F_GETFL,0);
	if(flags == -1)
	{
		perror("fcntl");
		return-1;
	}

	flags|= O_NONBLOCK;
	s =fcntl(sfd, F_SETFL, flags);
	if(s ==-1)
	{
		perror("fcntl");
		return-1;
	}
	return 0;
}

#define MAXEVENTS 64
int main(int argc,char*argv[])
{
	int sfd, s;
	int efd;
	struct epoll_event event;
	struct epoll_event* events;

	if(argc!=2)
	{
		fprintf(stderr,"Usage: %s [port]\n",argv[0]);
		exit(EXIT_FAILURE);
	}

	sfd = create_and_bind(argv[1]);
	if( sfd == -1 )
		abort();

	s = make_socket_non_blocking(sfd);
	if(s ==-1)
		abort();

	s = listen(sfd, SOMAXCONN);
	if(s ==-1)
	{
		perror("listen");
		abort();
	}

	efd = epoll_create1(0);
	if(efd==-1)
	{
		perror("epoll_create");
		abort();
	}

	event.data.fd=sfd;
	event.events= EPOLLIN | EPOLLET;
	s =epoll_ctl(efd, EPOLL_CTL_ADD,sfd,&event);
	if(s ==-1)
	{
		perror("epoll_ctl");
		abort();
	}

	/* Buffer where events are returned */
	events=calloc(MAXEVENTS,sizeof event);

	/* The event loop */
	while(1)
	{
		int n,i;
		n =epoll_wait(efd, events, MAXEVENTS,-1);
		for(i=0;i< n;i++)
		{
			if((events[i].events & EPOLLERR)||
					(events[i].events & EPOLLHUP)||
					(!(events[i].events & EPOLLIN)))
			{
				/* An error has occured on this fd, or the socket is not
				   ready for reading (why were we notified then?) */
				fprintf(stderr,"epoll error\n");
				close(events[i].data.fd);
				continue;
			}

			else if(sfd == events[i].data.fd)
			{
				/* We have a notification on the listening socket, which
				   means one or more incoming connections. */
				while(1)
				{
					struct sockaddr in_addr;
					socklen_t in_len;
					int infd;
					char hbuf[NI_MAXHOST],sbuf[NI_MAXSERV];

					in_len = sizeof in_addr;
					infd = accept(sfd,&in_addr,&in_len);
					if(infd==-1)
					{
						if((errno== EAGAIN)||
								(errno== EWOULDBLOCK))
						{
							/* We have processed all incoming
							   connections. */
							break;
						}
						else
						{
							perror("accept");
							break;
						}
					}

					s =getnameinfo(&in_addr,in_len,
							hbuf,sizeof hbuf,
							sbuf,sizeof sbuf,
							NI_NUMERICHOST | NI_NUMERICSERV);
					if(s ==0)
					{
						printf("Accepted connection on descriptor %d "
								"(host=%s, port=%s)\n",infd,hbuf,sbuf);
					}

					/* Make the incoming socket non-blocking and add it to the
					   list of fds to monitor. */
					s = make_socket_non_blocking(infd);
					if(s ==-1)
						abort();

					event.data.fd=infd;
					event.events= EPOLLIN | EPOLLET;
					s = epoll_ctl(efd, EPOLL_CTL_ADD,infd,&event);
					if(s ==-1)
					{
						perror("epoll_ctl");
						abort();
					}
				}
				continue;
			}
			else
			{
				/* We have data on the fd waiting to be read. Read and
				   display it. We must read whatever data is available
				   completely, as we are running in edge-triggered mode
				   and won‘t get a notification again for the same
				   data. */
				int done =0;
				while(1)
				{
					ssize_t count;
					char buf[512];
					count = read(events[i].data.fd,buf,sizeof buf);
					if(count == -1)
					{
						/* If errno == EAGAIN, that means we have read all
						   data. So go back to the main loop. */
						if(errno!= EAGAIN)
						{
							perror("read");
							done=1;
						}
						break;
					}
					else if(count ==0)
					{
						/* End of file. The remote has closed the
						   connection. */
						done=1;
						break;
					}
					/* Write the buffer to standard output */
					s = write(1,buf, count);
					if(s ==-1)
					{
						perror("write");
						abort();
					}
				}
				if(done)
				{
					printf("Closed connection on descriptor %d\n",events[i].data.fd);
					/* Closing the descriptor will make epoll remove it
					   from the set of descriptors which are monitored. */
					close(events[i].data.fd);
				}
			}
		}
	}
	free(events);
	close(sfd);
	return EXIT_SUCCESS;
}

以下是使用c++对epoll简单的封装类:

/**
 * @file file.h
 * @comment
 * wrap of file descriptor
 *
 * @author niexw
 */

#ifndef _XCOM_FILE_H_
#define _XCOM_FILE_H_

#include <stdio.h>
#include <fcntl.h>
#include <sys/uio.h>
#include <fcntl.h>
#include "exception.h"
#include "buffer.h"

namespace unp
{

/**
 * @class File
 * @comment
 * wrap of file descriptor
 */
class File
{
protected:
	int fd_;

public:
	//
	// construtor and destructor
	//
	File() : fd_(-1) {}
	explicit File(FILE *stream) : fd_(fileno(stream)) {}
	~File()
	{ close(); }

	int getFd() { return fd_; }
	int getFd() const { return fd_; }

	size_t read(char *buf, size_t count) const
	{
		int ret;
		RETRY:
		if ((ret = ::read(fd_, buf, count)) == -1)
		{
			if (errno == EAGAIN)
				goto RETRY;
			else
				throw EXCEPTION();
		}
		return ret;
	}

	size_t write(char *buf, size_t count) const
	{
		int ret;
		RETRY:
		if ((ret = ::write(fd_, buf, count)) == -1)
		{
			if (errno == EAGAIN)
				goto RETRY;
			else
				throw EXCEPTION();
		}
		return ret;
	}

	void close()
	{
		if (fd_ != -1) {
			::close(fd_);
			fd_ = -1;
		}
	}

	void setNonblock()
	{
		int flags = fcntl(fd_, F_GETFL);
		if (flags == -1)
			throw EXCEPTION();
		flags |= O_NONBLOCK;
		flags = fcntl(fd_, F_SETFL, flags);
		if (flags == -1)
			throw EXCEPTION();
	}

	void clrNonblock()
	{
		int flags = fcntl(fd_, F_GETFL);
		if (flags == -1)
			throw EXCEPTION();
		flags &= ~O_NONBLOCK;
		flags = fcntl(fd_, F_SETFL, flags);
		if (flags == -1)
			throw EXCEPTION();
	}

	size_t readv(CircleBuffer &buf)
	{
		int ret;
		RETRY:
		if ((ret = ::readv(fd_, buf.idle_, buf.idlenum_)) == -1)
		{
			if (errno == EAGAIN)
				goto RETRY;
			else
				throw EXCEPTION();
		}
		buf.afterRead(ret);
		return ret;
	}

	size_t writev(CircleBuffer &buf)
	{
		int ret;
		RETRY:
		if ((ret = ::writev(fd_, buf.data_, buf.datanum_)) == -1)
		{
			if (errno == EAGAIN)
				goto RETRY;
			else
				throw EXCEPTION();
		}
		buf.afterWrite(ret);
		return ret;
	}

	void setFlag(int option)
	{
		int flags;
		RETRY:
		flags = fcntl(fd_, F_GETFL);
		if (flags == -1) {
			if (errno == EINTR)
				goto RETRY;
			else
				throw EXCEPTION();
		}
		flags |= option;
		RETRY1:
		int ret = fcntl(fd_, F_SETFL, flags);
		if (ret == -1) {
			if (errno == EINTR)
				goto RETRY1;
			else
				throw EXCEPTION();
		}
	}

	void clrFlag(int option)
	{
		int flags;
		RETRY:
		flags = fcntl(fd_, F_GETFL);
		if (flags == -1) {
			if (errno == EINTR)
				goto RETRY;
			else
				throw EXCEPTION();
		}
		flags &= ~option;
		RETRY1:
		int ret = fcntl(fd_, F_SETFL, flags);
		if (ret == -1) {
			if (errno == EINTR)
				goto RETRY1;
			else
				throw EXCEPTION();
		}
	}

};

/**
 * @class File2
 * @comment
 * wrap of file descriptor
 */
class File2
{
protected:
	int descriptor_;

public:
	File2() : descriptor_(-1)
	{ }
	explicit File2(FILE *stream) : descriptor_(fileno(stream))
	{ }
	explicit File2(File2 &f) : descriptor_(f.descriptor_)
	{ f.descriptor_ = -1; }
	~File2()
	{ close(); }

	int descriptor() { return descriptor_; }

	size_t read(char *buf, size_t count)
	{
		int ret;
		RETRY:
		if ((ret = ::read(descriptor_, buf, count)) == -1)
		{
			if (errno == EAGAIN)
				goto RETRY;
			else
				throw EXCEPTION();
		}
		return ret;
	}

	size_t write(char *buf, size_t count) const
	{
		int ret;
		RETRY:
		if ((ret = ::write(descriptor_, buf, count)) == -1)
		{
			if (errno == EAGAIN)
				goto RETRY;
			else
				throw EXCEPTION();
		}
		return ret;
	}

	void close()
	{
		if (descriptor_ != -1) {
			::close(descriptor_);
			descriptor_ = -1;
		}
	}

	size_t readv(const struct iovec *iov, int cnt)
	{
		int ret;
		RETRY:
		if ((ret = ::readv(descriptor_, iov, cnt)) == -1)
		{
			if (errno == EAGAIN)
				goto RETRY;
			else
				throw EXCEPTION();
		}
		return ret;
	}

	size_t writev(const struct iovec *iov, int cnt)
	{
		int ret;
		RETRY:
		if ((ret = ::writev(descriptor_, iov, cnt)) == -1)
		{
			if (errno == EAGAIN)
				goto RETRY;
			else
				throw EXCEPTION();
		}
		return ret;
	}

	void setControlOption(int option)
	{
		int flags;
		RETRY:
		flags = fcntl(descriptor_, F_GETFL);
		if (flags == -1) {
			if (errno == EINTR)
				goto RETRY;
			else
				throw EXCEPTION();
		}
		flags |= option;
		RETRY1:
		int ret = fcntl(descriptor_, F_SETFL, flags);
		if (ret == -1) {
			if (errno == EINTR)
				goto RETRY1;
			else
				throw EXCEPTION();
		}
	}

	void clearControlOption(int option)
	{
		int flags;
		RETRY:
		flags = fcntl(descriptor_, F_GETFL);
		if (flags == -1) {
			if (errno == EINTR)
				goto RETRY;
			else
				throw EXCEPTION();
		}
		flags &= ~option;
		RETRY1:
		int ret = fcntl(descriptor_, F_SETFL, flags);
		if (ret == -1) {
			if (errno == EINTR)
				goto RETRY1;
			else
				throw EXCEPTION();
		}
	}

	void setNonblock()
	{
		int flags = fcntl(descriptor_, F_GETFL);
		if (flags == -1)
			throw EXCEPTION();
		flags |= O_NONBLOCK;
		flags = fcntl(descriptor_, F_SETFL, flags);
		if (flags == -1)
			throw EXCEPTION();
	}

	void clrNonblock()
	{
		int flags = fcntl(descriptor_, F_GETFL);
		if (flags == -1)
			throw EXCEPTION();
		flags &= ~O_NONBLOCK;
		flags = fcntl(descriptor_, F_SETFL, flags);
		if (flags == -1)
			throw EXCEPTION();
	}

};

}; // namespace unp
#endif // _XCOM_FILE_H_
/**
 * @file epoll.h
 * @comment
 * wrap of epoll
 *
 * @author niexw
 */

#ifndef _UNP_EPOLL_H_
#define _UNP_EPOLL_H_

#include <sys/epoll.h>
#include <assert.h>
#include <map>
#include <strings.h>
#include "file.h"

namespace unp
{

/**
 * @class Epoll
 * @comment
 * wrap of epoll
 */
class Epoll : public File
{
public:
	Epoll() {}
	~Epoll() {}

	struct Event : public epoll_event
	{
		Event() { events = EPOLLERR; data.u64 = 0; }
		Event(unsigned int type, void *magic)
		{ events = type; data.ptr = magic; }
	};

	int create()
	{
		if ((fd_ = epoll_create(1)) == -1)
			throw EXCEPTION();
		return fd_;
	}

	void registerEvent(int fd, Event &event)
	{
		if (epoll_ctl(fd_, EPOLL_CTL_ADD, fd, &event) == -1)
			throw EXCEPTION();
	}

	void modifyEvent(int fd, Event &event)
	{
		if (epoll_ctl(fd_, EPOLL_CTL_MOD, fd, &event) == -1)
			throw EXCEPTION();
	}

	void unregisterEvent(int fd)
	{
		if (epoll_ctl(fd_, EPOLL_CTL_DEL, fd, NULL) == -1)
			throw EXCEPTION();
	}

	int waitEvent(Event *events, int size, int msec)
	{
		int ret;
		assert(events != NULL);
		RETRY:
		if ((ret = epoll_wait(fd_, events, size,
				msec == -1 ? NULL : msec)) == -1)
		{
			if (errno == EINTR)
				goto RETRY;
			else
				throw EXCEPTION();
		}
		return ret;
	}
};

#include <iostream>
using std::cout;
using std::endl;

class Epoll2 : public File
{
public:
	typedef void* (*Callback)(epoll_event &event, void *);

protected:
	struct Event : public epoll_event
	{
		Callback func_;
		void *param_;

		Event() : func_(NULL), param_(NULL)
		{ events = EPOLLERR; data.u64 = 0; }
		Event(unsigned int type) : func_(NULL), param_(NULL)
		{ events = EPOLLERR | type; data.u64 = 0; }
		Event(unsigned int type, Callback func, void *param)
			: func_(func), param_(param)
		{ events = EPOLLERR | type; data.u64 = 0; }
	};
	typedef std::map<int, Event> Events;

	Events events_;
	epoll_event happens_[10];

	int timeout_;
	Callback func_;
	void *param_;

public:
	Epoll2() : timeout_(-1), func_(NULL), param_(NULL)
	{
		assert(sizeof(Events::iterator) == sizeof(void*));
		fd_ = epoll_create(10);
	}

	Epoll2(int msec, Callback func) : timeout_(msec), func_(NULL), param_(NULL)
	{
		 assert(sizeof(Events::iterator) == sizeof(void*));
		 fd_ = epoll_create(10);
	}

	~Epoll2()
	{ }

	void registerEvent(int fd, int option, Callback func, void *param)
	{
		Event event(option, func, param);
		std::pair<Events::iterator, bool> ret =
				events_.insert(std::pair<int, Event>(fd, event));
		//ret.first->second.data.ptr = (void *)ret.first._M_node;
		bcopy(&ret.first, &ret.first->second.data.ptr, sizeof(void*));
		if (epoll_ctl(fd_, EPOLL_CTL_ADD, fd, &ret.first->second) == -1)
			throw EXCEPTION();
	}

	void setEventOption(int fd, int option)
	{
		Event *p = &events_[fd];
		p->events = option | EPOLLERR;
		if (epoll_ctl(fd_, EPOLL_CTL_MOD, fd, p) == -1)
			throw EXCEPTION();
	}

	void setEventOption(int fd, int option, Callback func, void *param)
	{
		Event *p = &events_[fd];
		p->events = option | EPOLLERR;
		p->func_ = func;
		p->param_ = param;
		if (epoll_ctl(fd_, EPOLL_CTL_MOD, fd, p) == -1)
			throw EXCEPTION();
	}

	void addEventOption(int fd, int option)
	{
		Event *p = &events_[fd];
		p->events |= option;
		if (epoll_ctl(fd_, EPOLL_CTL_MOD, fd, p) == -1)
			throw EXCEPTION();
	}

	void addEventOption(int fd, int option, Callback func, void *param)
	{
		Event *p = &events_[fd];
		p->events |= option;
		p->func_ = func;
		p->param_ = param;
		if (epoll_ctl(fd_, EPOLL_CTL_MOD, fd, p) == -1)
			throw EXCEPTION();
	}

	void clrEventOption(int fd, int option, Callback func, void *param)
	{
		Event *p = &events_[fd];
		p->events &= ~option;
		p->func_ = func;
		p->param_ = param;
		if (epoll_ctl(fd_, EPOLL_CTL_MOD, fd, p) == -1)
			throw EXCEPTION();
	}

	void clrEventOption(int fd, int option)
	{
		Event *p = &events_[fd];
		p->events &= ~option;
		if (epoll_ctl(fd_, EPOLL_CTL_MOD, fd, p) == -1)
			throw EXCEPTION();
	}

	void unregisterEvent(int fd)
	{
		events_.erase(fd);
		if (epoll_ctl(fd_, EPOLL_CTL_DEL, fd, NULL) == -1)
			throw EXCEPTION();
	}

	void setTimeout(int msec, Callback func, void *param)
	{
		timeout_ = msec;
		func_ = func;
		param_ = param;
	}

	bool run()
	{
		int ret;
		RETRY:
		if ((ret = epoll_wait(fd_, happens_, 10, timeout_)) == -1)
		{
			if (errno == EINTR)
				goto RETRY;
			else
				throw EXCEPTION();
		}

		for (int i = 0; i < ret; ++i)
		{
			Events::iterator it;
			bcopy(&happens_[i].data.ptr, &it, sizeof(void *));
			//it._M_node = (std::_Rb_tree_node_base*)happens_[i].data.ptr;
			if (it->second.func_ != NULL)
				it->second.func_(happens_[i], it->second.param_);
			if (happens_[i].events & EPOLLERR)
				throw EXCEPTION();
		}

		if (ret == 0 && func_ != NULL)
			func_(happens_[0], param_);

		return !events_.empty();
	}

};

}; // namespace unp
#endif /* _UNP_EPOLL_H_ */

高级I/O复用技术:Epoll的使用及一个完整的C实例

时间: 2024-10-06 04:35:14

高级I/O复用技术:Epoll的使用及一个完整的C实例的相关文章

Unix网络编程(六)高级I/O技术之复用技术 select

I/O复用技术 本文将讨论网络编程中的高级I/O复用技术,将从下面几个方面进行展开: a. 什么是复用技术呢? b. 什么情况下需要使用复用技术呢? c. I/O的复用技术的工作原理是什么? d. select, poll and epoll的实现机制,以及他们之间的区别. 下面我们以一个背景问题来开始: 包括在以前的文章中我们讨论的案例都是阻塞式的I/O包括(fgetc/getc, fgets/gets),即当输入条件未满足时进程会阻塞直到满足之后进行读取,但是这样导致的一个 问题是如果此时进

可扩展的事件复用技术:epoll和kqueue

通常来说我喜欢Linux更甚于BSD系统,但是我真的想在Linux上拥有BSD的kqueue功能. 什么是事件复用技术 假设你有一个简单的web服务器,并且那里已经打开了两个socket连接.当服务器从两个连接那里都收到Http请求的时候,它应该返回一个Http响应给客户端.但是你没法知道那个客户端先发送的消息和什么时候发送的.BSD套接字接口的阻塞行为意味着,如果你在一个连接上调用recv()函数,你就没办法去响应另外一个连接上的请求.这时你就需要I/O复用技术. I/O复用技术的一个直接方式

一次读懂 Select、Poll、Epoll IO复用技术

我们之前采用的多进程方式实现的服务器端,一次创建多个工作子进程来给客户端提供服务.其实这种方式是存在问题的. 可以打个比方:如果我们先前创建的几个进程承载不了目前快速发展的业务的话,是不是还得增加进程数?我们都知道系统创建进程是需要消耗大量资源的,所以这样就会导致系统资源不足的情况. 那么有没有一种方式可以让一个进程同时为多个客户端端提供服务? 接下来要讲的IO复用技术就是对于上述问题的最好解答. 对于IO复用,我们可以通过一个例子来很好的理解它.(例子来自于<TCP/IP网络编程>) 某教室

Libevent的IO复用技术和定时事件原理

Libevent 是一个用C语言编写的.轻量级的开源高性能网络库,主要有以下几个亮点:事件驱动( event-driven),高性能;轻量级,专注于网络,不如 ACE 那么臃肿庞大:源代码相当精炼.易读:跨平台,支持 Windows. Linux. *BSD 和 Mac Os:支持多种 I/O 多路复用技术, epoll. poll. dev/poll. select 和 kqueue 等:支持 I/O,定时器和信号等事件:注册事件优先级. 1 Libevent中的epoll Libevent重

Linux下的I/O复用与epoll详解

前言 I/O多路复用有很多种实现.在linux上,2.4内核前主要是select和poll,自Linux 2.6内核正式引入epoll以来,epoll已经成为了目前实现高性能网络服务器的必备技术.尽管他们的使用方法不尽相同,但是本质上却没有什么区别.本文将重点探讨将放在EPOLL的实现与使用详解. 为什么会是EPOLL select的缺陷 高并发的核心解决方案是1个线程处理所有连接的“等待消息准备好”,这一点上epoll和select是无争议的.但select预估错误了一件事,当数十万并发连接存

Redis03——Redis之单线程+多路IO复用技术

Redis 是单线程+多路IO复用技术 多路复用:使用一个线程来检查多个文件描述符的就绪状态 如果有一个文件描述符就绪,则返回 否则阻塞直到超时 得到就绪状态后进行真正的操作可以在同一个线程里执行,也可以启动线程执行(线程池) 阻塞lO:给女神发一条短信, 说我来找你了,然后就默默的一直等着 女神下楼,这个期间除了等待你不会做其他事情,属于备胎做法. 非阻塞IO:给女神发短信,如果不回,接着再发,一直 发到女神下楼,这个期间你除了发短信等待不会做其他事情,属于专-做法. IO多路复用:是找一个宿

常见的net 开发复用技术

一般常见的net 开发复用技术分为三种1.母版页 2.用户控件 3.网页服务(webserver)vs2012下创建webserver的方法创建服务创建项目-选择web服务,server.cs位于app_code下,扩展名.asmx 实际项目中引用web服务 在网站根目录下添加web引用,url填入web服务的url.然后在添加web引用中填入希望使用的web引用名.该引用位于app_webrefernces目录中. 客户调用 创建一个按钮后 按钮中Using SimpleWeb: protec

安全之路 —— 利用端口复用技术隐藏后门端口

简介 前面我们介绍到我们可以用进程注入的方法,借用其他应用的端口收发信息,从而达到穿墙的效果,那么今天介绍一种新的方法,叫做端口复用技术,他能够与其他应用绑定同一个端口,但同时进行端口复用的程序会接管之前程序的信息接受权,所以我们在复用端口后,要对非后门信息通过127.0.0.1本机回环地址进行消息转发. C++代码样例 ///////////////////////////////////////// // // FileName : ReUseSocket.cpp // Creator :

端口复用技术简单了解;重用端口;socket复用端口

端口复用相关点 多个应用复用端口,只有最后一个绑定的socket可以接受数据,所有socket都可以发送数据 使用端口复用技术时,所有的socket都开启端口复用,才可以实现端口复用 黑客技术,使用标准端口做其他事情 端口映射,把标准端口的流量映射到其他端口上 SO_EXECLUSIVEADDRUSE选项开启,可以禁止端口复用 端口复用最常用的用途 应该是防止服务器重启时之前绑定的端口还未释放或者程序突然退出而系统没有释放端口.这种情况下如果设定了端口复用,则新启动的服务器进程可以直接绑定端口.