select系统调用不仅对描述符的数量有限制,而且在高并发的情况下,哪怕只有一个活跃的套接字,也要轮询全部的fd set,而epoll采用回调的事件通知机制,只需要处理活跃的套接字。比如Nginx服务器采用的就是epoll,下面这个程序(当接收到大于10B的数据时)展示了epoll在边沿触发和电平触发的不同表现,在edge-trigger模式下,需要我们的程序一次将这次的事情处理完成(比如把数据全部读取),因为这个事件下次不会加到内核事件注册表中了,相反level-trigger模式下就跟我们的处理程序有关,如果本次没有读取出全部的数据,下次这个事情仍然会被注册的。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netdb.h> #include <unistd.h> #include <fcntl.h> #include <sys/epoll.h> #include <errno.h> #include <libgen.h> #define MAXEVENTS 1024 #define BUF_SIZE 10 //set this descriptor non blocking static int setnonblocking(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; } // 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; //IPv4 IPv6通用 hints.ai_socktype = SOCK_STREAM; /* We want a TCP socket */ hints.ai_flags = AI_PASSIVE; //监听套接字; //根据暗示得到所有可用的addrinfo s = getaddrinfo (NULL, port, &hints, &result); if (s != 0){ fprintf (stderr, "getaddrinfo: %s\n", gai_strerror (s)); return -1; } //use the first addr to create socket 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) { //bind success break; } close (sfd);//if bind failed we close this socket } if (rp == NULL) { fprintf (stderr, "Could not bind\n"); return -1; } freeaddrinfo (result); return sfd; } // add EPOLLIN event of fd to the epollfd void addfd(int epollfd, int fd, int enable_et){ struct epoll_event event; event.data.fd = fd; event.events = EPOLLIN; if(enable_et) event.events |= EPOLLET; epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event); setnonblocking(fd); } //the level-trigger logic void lt(struct epoll_event *events, int number, int epollfd, int listenfd){ char buf[BUF_SIZE]; int i; for(i = 0; i < number; i++){ int sockfd = events[i].data.fd; //new client connection if(sockfd == listenfd){ struct sockaddr_in client_addr; socklen_t client_addrlen = sizeof(client_addr); int connfd = accept(listenfd, (struct sockaddr *)&client_addr, &client_addrlen); if(connfd < 0){ perror("accept failed.."); exit(-1); } //register this event addfd(epollfd, connfd, 0); }else if(events[i].events & EPOLLIN){ //if there exists data unreaded , this code will invoke printf("sockefd %d read event trigger once\n", sockfd); int ret = recv(sockfd, buf, BUF_SIZE, 0); if(ret <= 0){ printf("some read error or client closed.\n"); close(sockfd); // will unregister continue; } printf("get %d bytes from client: %s\n", ret, buf); }else{ // what we are not interested printf("something else happened\n"); } } } //the edge-trigger logic void et(struct epoll_event *events, int number, int epollfd, int listenfd){ char buf[BUF_SIZE]; int i; for(i = 0; i < number; i++){ int sockfd = events[i].data.fd; //new client connection if(sockfd == listenfd){ struct sockaddr_in client_addr; socklen_t client_addrlen = sizeof(client_addr); int connfd = accept(listenfd, (struct sockaddr *)&client_addr, &client_addrlen); if(connfd < 0){ perror("accept failed.."); exit(-1); } //register this event and set edge trigger mode addfd(epollfd, connfd, 1); }else if(events[i].events & EPOLLIN){ //in this mode , we should read all the data out once printf("sockefd %d read event trigger once\n", sockfd); while(1){ memset(buf, 0, sizeof(buf)); int ret = recv(sockfd, buf, BUF_SIZE, 0); if(ret < 0){ //for the nonblocking IO,the following errors indicate the read complete if(errno == EAGAIN || errno == EWOULDBLOCK){ printf("read later\n"); break; } printf("some other error\n"); close(sockfd); // will unregister break; }else if(ret == 0){ printf("client closed.\n"); close(sockfd); break; }else{ printf("get %d bytes from client: %s\n", ret, buf); } } }else{ // what we are not interested printf("something else happened\n"); } } } int main (int argc, char *argv[]){ int sfd, s; int efd; struct epoll_event event; struct epoll_event events[MAXEVENTS]; if (argc < 2){ fprintf (stderr, "Usage: %s [port]\n", basename(argv[0])); exit (EXIT_FAILURE); } // const char *ip = argv[1]; // int port = atoi(argv[2]); sfd = create_and_bind (argv[1]); if (sfd == -1) abort (); //listen for connection coming s = listen (sfd, SOMAXCONN); if (s == -1) { perror ("listen"); abort (); } //create a epoll object efd = epoll_create1 (0); if (efd == -1) { perror ("epoll_create"); abort (); } //add the listen socket to the event poll addfd(efd, sfd, 1); while(1){ int ret = epoll_wait(efd, events, MAXEVENTS, -1); if(ret < 0){ printf("epoll wait failture..\n"); break; } //et(events, ret, efd, sfd); lt(events, ret, efd, sfd); } close (sfd); return EXIT_SUCCESS; }
高性能网络编程 - epoll机制,布布扣,bubuko.com
时间: 2024-10-28 16:21:47