text: #ifndef poll_socket_epoll_h
  #define poll_socket_epoll_h
  
  #include <netdb.h>
  #include <unistd.h>
  #include <sys/epoll.h>
  #include <sys/types.h>
  #include <sys/socket.h>
  #include <netinet/in.h>
  #include <arpa/inet.h>
  #include <fcntl.h>
  
  static bool 
  sp_invalid(int efd) {
  	return efd == -1;
  }
  
  static int
  sp_create() {
  	/**
  	 * 创建一个epoll的句柄，size用来告诉内核这个监听的数目一共有多大。这个参数不同于select()中的第一个参数，给出最大监听的fd+1的值。
  	 * 需要注意的是，当创建好epoll句柄后，它就是会占用一个fd值，在linux下如果查看/proc/进程id/fd/，是能够看到这个fd的，
  	 * 所以在使用完epoll后，必须调用close()关闭，否则可能导致fd被耗尽。
  	*/
  	return epoll_create(1024);
  }
  
  static void
  sp_release(int efd) {
  	close(efd);
  }
  
  static int 
  sp_add(int efd, int sock, void *ud) {
  	struct epoll_event ev;
  	ev.events = EPOLLIN;
  	ev.data.ptr = ud;
  	if (epoll_ctl(efd, EPOLL_CTL_ADD, sock, &ev) == -1) {
  		return 1;
  	}
  	return 0;
  }
  
  static void 
  sp_del(int efd, int sock) {
  	epoll_ctl(efd, EPOLL_CTL_DEL, sock , NULL);
  }
  
  static void 
  sp_write(int efd, int sock, void *ud, bool enable) {
  	struct epoll_event ev;
  	ev.events = EPOLLIN | (enable ? EPOLLOUT : 0);
  	ev.data.ptr = ud;
  	epoll_ctl(efd, EPOLL_CTL_MOD, sock, &ev);
  }
  
  static int 
  sp_wait(int efd, struct event *e, int max) {
  	struct epoll_event ev[max];
  	int n = epoll_wait(efd , ev, max, -1);
  	int i;
  	for (i=0;i<n;i++) {
  		e[i].s = ev[i].data.ptr;
  		unsigned flag = ev[i].events;
  		e[i].write = (flag & EPOLLOUT) != 0;
  		e[i].read = (flag & (EPOLLIN | EPOLLHUP)) != 0;
  		e[i].error = (flag & EPOLLERR) != 0;
  		e[i].eof = false;
  	}
  
  	return n;
  }
  
  static void
  sp_nonblocking(int fd) {
  	int flag = fcntl(fd, F_GETFL, 0);
  	if ( -1 == flag ) {
  		return;
  	}
  
  	fcntl(fd, F_SETFL, flag | O_NONBLOCK);
  }
  
  #endif
  